0

i'm on

  • .net Core 3.0 web api
  • token jwt / owin
  • EF Core 3

I have a multi-Database project. User choose a database from list, in the login form. After that i set the dbName/connectionString in the "selectedDb" claim

In every controller i have 8 to 20 classes (manager) that needs DbContext as parameter contructor => i can't create an istance of managers or dbContext in the controller constructor, because i do not have the login token yet!

So that , in every Action i create an istance of dbContext(token provide connection string) and managers istance... But it means i have to "copy/paste" the same 3/4 line of code in every action

how can i provide a valid instance of db context? maybe using middleware or custom authorization attribute

Is there any way to create instance of dbcontext in controller constructor ? (with "dynamic connection string" provided from token)

Some code example

Init classes function (avoid to copy 16&3#92 lines every times)

private DatabaseContext InitContextAndManager(string connectionString)
{
    _dbContext = new DatabaseContext(connectionString);

    _someManager1 = new SomeManager_1(_dbContext);
    _someManager2 = new SomeManager_2(_dbContext);
    _someManager3 = new SomeManager_3(_dbContext);
    _someManager4 = new SomeManager_4(_dbContext);
    _someManager5 = new SomeManager_5(_dbContext);
    _someManager6 = new SomeManager_6(_dbContext);
    _someManager7 = new SomeManager_7(_dbContext);
    _someManager8 = new SomeManager_8(_dbContext);
    _someManager9 = new SomeManager_9(_dbContext);
    _someManager10 = new SomeManager_10(_dbContext);
    _someManager11 = new SomeManager_11(_dbContext);
    _someManager12 = new SomeManager_12(_dbContext);
    _someManager13 = new SomeManager_13(_dbContext);
    _someManager14 = new SomeManager_14(_dbContext);
    _someManager15 = new SomeManager_15(_dbContext);
}

Some Api Example

[Authorize]
public ActionResult Api_1()
{
    var connectionString = User.Identity.GetConnectionString();
    InitContextAndManager(connectionString);

    //some api_1 stuff
}

[Authorize]
public ActionResult Api_2()
{
    var connectionString = User.Identity.GetConnectionString();
    InitContextAndManager(connectionString);

    //some api_2 stuff
}

[Authorize]
public ActionResult Api_3()
{
    var connectionString = User.Identity.GetConnectionString();
    InitContextAndManager(connectionString);

    //some api_3 stuff
}
Max94
  • 121
  • 1
  • 12
  • Possible duplicate of [Configure connection string from controller (ASP.NET Core MVC 2.1)](https://stackoverflow.com/questions/54490808/configure-connection-string-from-controller-asp-net-core-mvc-2-1) –  Oct 03 '19 at 17:38
  • Take a look at my answer [here](https://stackoverflow.com/questions/54490808/configure-connection-string-from-controller-asp-net-core-mvc-2-1/54497955#54497955). In DbConnectionInfo you can access the claims and set the connectionstring. –  Oct 03 '19 at 17:38
  • sorry but I do not understand what you mean @RuardvanElburg – Max94 Oct 04 '19 at 08:02
  • @RuardvanElburg details added – Max94 Oct 04 '19 at 10:31
  • _ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies._ From the [documentation](https://learn.microsoft.com/aspnet/core/fundamentals/dependency-injection). In that pattern you don't create objects (_new_ keyword) but inject them. My answer is based on DI. –  Oct 04 '19 at 11:04
  • DI is not a problem for me, i need help for the database istance and connectionString from claim. I already use DI pattern – Max94 Oct 04 '19 at 11:27
  • @RuardvanElburg i don't think your answer will help me – Max94 Oct 04 '19 at 12:42
  • Try the code In the update of the answer. Change `MyContext = "";` to `MyContext = user.Identity.GetConnectionString();`. What happens is that you use middleware to set the connectionstring to the value of the claim, before the context is accessed. When DI creates the context, the options are injected in the constructor of the context. The context should be injected in SomeManager_x. In order of dependency the objects are created. All you have to do is register the services in startup and inject the objects using the constructor of the object / service. –  Oct 04 '19 at 13:04
  • how can i inject a valid instance of db context if i do not have the connection string ?? – Max94 Oct 04 '19 at 13:21
  • uhmmm , i understand what you mean.... let me try to do it – Max94 Oct 04 '19 at 13:31
  • Uhm ok , it works. But what about unauthorized api? how can i return 401 if i do not have connection string?(invalid token ) – Max94 Oct 04 '19 at 13:56
  • With an invalid token the pipeline is short-circuited, meaning that the request won't even hit the secured controlller. The example concentrates on how to set the connectionstring, but you should add middleware that short-circuits the pipeline (!next, as explained [here](https://learn.microsoft.com/aspnet/core/fundamentals/middleware/)) in case the connectionstring is not available. because of a missing claim, a missing value, not authorized (which should not be possible if configured correctly) or is invalid. –  Oct 04 '19 at 15:02

1 Answers1

0

With @Ruard van Elburg help and His Answer

here the solution

public ControllerConstructor(DbConnectionInfo db)
{
    _databaseContext = db.DbContext;
    _someManager1 = new SomeManager(_dbcontext);
}
public class DbConnectionInfo
{
    public DatabaseContext DbContext { get; set; }

    public DbConnectionInfo(IHttpContextAccessor httpContextAccessor)
    {
        var user = httpContextAccessor.HttpContext.User;
        //for question example
        DbContext = new DatabaseContext(user.Identity.GetConnectionString);
    }
}
[Authorize]
public ActionResult Api_1()
{
    //some api_1 stuff
}
Max94
  • 121
  • 1
  • 12
  • SomeManager should be injected in the controller e.g. `public MyController(SomeManager someManager)`. The constructor of SomeManager is: `public SomeManager(SomeContext someContext)`, where the SomeContext constructor is: `public SomeContext(DbConnectionInfo)`. This last class is automatically resolved by DI, if configured properly. If you receive an error, then it may be that a certain service couldn't be resolved because it wasn't added to DI. With DI there's no need for the `new` keyword. It's alright to create a DTO with `new`, but not services. In Api_1: `_someManager1.DoSomething();` –  Oct 05 '19 at 08:42
  • Some Manager 1 could exist only in controller 1 and 3, some manager 2/3 in controller 1 2 4 5 – Max94 Oct 05 '19 at 08:44