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.