1. Thomas Rolseth
  2. SnapDevelop
  3. Thursday, 17 September 2020 07:35 AM UTC

In a SnapDevelop API solution I am working on, I have the following in my startup.cs that handles the database connection:

 

services.AddDataContext(m => m.UseOdbc(Configuration["ConnectionStrings:temp"]));

 

The DSN 'temp' is in my appsettings.json file and my database context class is attached.  There could be multiple PowerBuilder clients calling this API that would need to connect to a different database.  I'm using ODBC in my example but all of these databases will be SQL Server.  If I put a client ID in the request header or pass a client ID to a controller in the url for the RestClient call, how could I use that to switch between connection strings?

 

Thanks, Tom

 

Attachments (1)
Accepted Answer
Logan Liu @Appeon Accepted Answer Pending Moderation
  1. Saturday, 19 September 2020 16:37 PM UTC
  2. SnapDevelop
  3. # Permalink

Hi Thomas,

There was a resolved Q&A that provides a sample project for you to create DataContext dynamically.:

Dynamic connections with SnapObjects.

Regards,

Logan

Comment
  1. Thomas Rolseth
  2. Thursday, 24 September 2020 14:47 PM UTC
A follow-up question on this. What if I wanted to pass the client/customer ID in a datapacker object that I extract in the controller? The DataFactory example makes a connection to the db before the controller is ever called based on a param in the querystring.



So I get the ID from the datapacker in the controller, pass that value to a service and connect to the db at the beginning of the service. How would you code the service to make a call to the CreateDataContext() method (i.e. the one in your DataContextFactory class)?



Thanks, Tom
  1. Helpful
  1. Thomas Rolseth
  2. Thursday, 24 September 2020 14:52 PM UTC
[HttpPost]

[ProducesResponseType(StatusCodes.Status500InternalServerError)]

public ActionResult<IDataPacker> Foo(IDataUnpacker unpacker)

{

var packer = new DataPacker();



var tenantID = unpacker.GetValue<string>("tenant");



try

{

//pass tenant code to service and connect to db there

var rc = _service.FooMethod(tenantID);

}

catch (Exception e)

{

return StatusCode(StatusCodes.Status500InternalServerError, e.Message);

}



return packer;

}
  1. Helpful
  1. Logan Liu @Appeon
  2. Friday, 25 September 2020 02:05 AM UTC
Hi Tom,

You can also get data from request body. Please refer to the following code:



var body = _httpAccessor.HttpContext.Request.Body;

string data;



using (StreamReader reader = new StreamReader(body,Encoding.UTF8,true,1024,true) )

{

data = reader.ReadToEnd();

}



var packer = new DataPacker(data, DataFormat.Json); // Then you can get value from this object



Regards,

Logan
  1. Helpful
There are no comments made yet.
armando herrera @Appeon Accepted Answer Pending Moderation
  1. Friday, 18 September 2020 23:11 PM UTC
  2. SnapDevelop
  3. # 1

Hi Thomas!

I'm not sure if this is what you are looking for? 

The key is on the Service* (Take a look the If case)

Steps:

1.- Created the DataContexts (2) (Assuming the two tables on the two databases are identical structure)

Refer: Add a Database Context

2.- Create the Model

/////////////// CustomersModel.cs

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using SnapObjects.Data;
using DWNet.Data;

namespace restapi.Models
{
    [DataWindow("d_customers", DwStyle.Grid)]
    [Table("customer")]
    #region DwSelectAttribute  
    [DwSelect("PBSELECT( VERSION(400) TABLE(NAME=\"customer\" ) @(_COLUMNS_PLACEHOLDER_) )")]
    #endregion
    [DwKeyModificationStrategy(UpdateSqlStrategy.DeleteThenInsert)]
    [UpdateWhereStrategy(UpdateWhereStrategy.KeyAndConcurrencyCheckColumns)]
    public class CustomersModel
    {
        [Key]
        [DwColumn("customer", "id")]
        public int Id { get; set; }

        [ConcurrencyCheck]
        [StringLength(15)]
        [DwColumn("customer", "fname")]
        public string Fname { get; set; }

        [ConcurrencyCheck]
        [StringLength(20)]
        [DwColumn("customer", "lname")]
        public string Lname { get; set; }

        [ConcurrencyCheck]
        [StringLength(20)]
        [DwColumn("customer", "city")]
        public string City { get; set; }

        [ConcurrencyCheck]
        [StringLength(35)]
        [DwColumn("customer", "company_name")]
        public string Company_Name { get; set; }

    }

}

 

3.- Scaffolding (Service and Controller from Model SqlModelMapper)

4.- Change code on the Service and on the Interface


///////////Service*

public class CustomersService : ICustomersService
{
private readonly AppeonSampleDataContext _dataContext;
private readonly AppeonSampleADataContext _dataContextA;

public CustomersService(AppeonSampleDataContext dataContext, AppeonSampleADataContext adataContext)
{
_dataContext = dataContext;
_dataContextA = adataContext;
}

public IList<CustomersModel> Retrieve(int clientID)
{
if(clientID == 0)
return _dat aContextA.SqlModelMapper.Load<CustomersModel>().ToList();
else
return _dataContext.SqlModelMapper.Load<CustomersModel>().ToList();
}

 

/////////////////////Interfase

public interface ICustomersService
{

IList<CustomersModel> Retrieve(int clientID);

}


///////////////// Controller

// In the controller added {clientId} on the HttpGet Method

// URL: "http://localhost:5000/api/Customers/Retrieve/1"

 //GET api/Customers/Retrieve/clientId
[HttpGet("{clientId}")]
[ProducesResponseType(typeof(IList<CustomersModel>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public ActionResult<IList<CustomersModel>> Retrieve(int clientId)
{
try {
var result = _icustomersservice.Retrieve(clientId);
Return Ok(result);
}
catch (Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
}
}

6. My final startup.cs 

///////////////////////////////// Startup.cs 

// Startup.cs with the 2 DataContexts added

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(m =>
            {
                m.UseCoreIntegrated();
                m.UsePowerBuilderIntegrated();
            })
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            services.AddDataContext<AppeonSampleADataContext>(m => m.UseSqlServer(this.Configuration,              "AppeonSample"));
            services.AddDataContext<AppeonSampleDataContext>(m => m.UseSqlServer(this.Configuration, "AppeonSample"));
            services.AddScoped<ICustomersService, CustomersService>();

            services.AddGzipCompression(CompressionLevel.Fastest);

 

7. Create a client on PowerBuilder


////////////////Client ///////////////////

// Power Builder as Rest Client /

RestClient lrc_Client
String ls_Url, ls_Method
Long ll_rtn

//test 1: 

ls_Url = "http://localhost:5000/api/Customers/Retrieve/1"

//test 2: 

// ls_Url = "http://localhost:5000/api/Customers/Retrieve/0"

dw_customers.clear( )

lrc_Client = Create RestClient
ls_Method = "GET"
lrc_Client.SetRequestHeader ("Content-Type", "application/json")
lrc_Client.SetRequestHeader("Accept-Encoding", "gzip")

ll_rtn = lrc_Client.Retrieve( dw_customers,ls_Url )
If ll_rtn >= 0 And lrc_Client.GetResponseStatusCode() = 200 Then
MessageBox( "Retrieve Success","Rows:" + String ( ll_rtn ))
Else
MessageBox(
"Retrieve Failed","Rows:" + String ( ll_rtn ))
End If

If IsValid (lrc_Client) Then Destroy ( lrc_Client )

 

////////////////////////////////////////////////////

Hope it works for you! 

 

Attachments (1)
Comment
There are no comments made yet.
Thomas Rolseth Accepted Answer Pending Moderation
  1. Thursday, 17 September 2020 16:17 PM UTC
  2. SnapDevelop
  3. # 2

Armando, 

Attached is zip file containing the files you asked for along with other related classes.  I was thinking of sending a client ID in the request header and storing that using middleware (see startup.cs and logging.cs).  Then using the OnConfiguring method to switch between databases/connection strings.  I've been unable to get the commented code in SampleDataContext.cs to work however.  The UploadImportFile is a method in SampleController.cs that would need to switch databases depending on which client app called it.  Any examples/suggestions you could provide would be great.

 

Thanks, Tom

Attachments (1)
Comment
There are no comments made yet.
armando herrera @Appeon Accepted Answer Pending Moderation
  1. Thursday, 17 September 2020 15:49 PM UTC
  2. SnapDevelop
  3. # 3

Hi Thomas, 

Is it possible to share with us your appsettings.json file and your database context class?. Just to be able to see what you are doing but I don't see too much of a problem in putting one more variable to the URL and making a switch case in the controller and use as many datacontext as databases you want to connect.

 

Regards! 

Comment
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.
We use cookies which are necessary for the proper functioning of our websites. We also use cookies to analyze our traffic, improve your experience and provide social media features. If you continue to use this site, you consent to our use of cookies.