0

I'm trying to get the database name from the request url because i have more than one database

so, i have x1.com, x2.com and x3.com i want the DbContext to generate the connection string from the url at the runtime

what i did is that

  public SaaSZeroDbContext()
       : base(GetConnectionStringFromUrl())
    {

    }

    //function to get the connection string 
    private static string GetConnectionStringFromUrl()
    {
        var url = HttpContext.Current.Request.Url;
        string connectionString = @"Data Source=.\MSSQLSERVER2016; Database={DatabaseName}; Integrated Security=True;MultipleActiveResultSets=True";

        var databaseName = ""; // you should extract the database name from url here
        connectionString = connectionString.Replace("{DatabaseName}", databaseName);
        return connectionString;
    }

but i'm getting error HttpContext.Current is null, why HttpContext.Current is null ?

5
  • Pass the HttpContext from the controller to your method as an argument Commented Dec 20, 2017 at 23:09
  • The controller in a tier and GetConnectionStringFromUrl is in another tier, i'm using aspnetboilerplate Template, how to do that ? Commented Dec 20, 2017 at 23:23
  • 1
    I typed and deleted a long answer. The short version is that I don't recommend determining your database from your URL. That's an entirely separate layer of your application. Getting it from the URL effectively allows the user to decide which database they should use. Even if you have plenty of good security to prevent misuse it's not a recommendable pattern. What distinguishes one request that goes to one database from another that goes to another database? I'd start there and see how your controller can figure out how to pass the right information to the next layer. Commented Dec 20, 2017 at 23:46
  • @ScottHannen I don't think OP allows url to control DB connection string, just pick one from list of well known once, which does not really have any security issues you are talking about. Commented Dec 20, 2017 at 23:56
  • 1
    My concern isn't primarily even security. It's more that the database is a different layer. At the level of the browser or even the controller, there isn't (or shouldn't be) awareness that different databases or even any databases even exist. It's all abstractions. Commented Dec 21, 2017 at 0:01

2 Answers 2

1

Your approach is not true by design. I will explain why.

This is the DbContext comes with the template:

public class AbpProjectNameDbContext : AbpZeroDbContext<Tenant, Role, User>
{
    //TODO: Define an IDbSet for your Entities...

    /* NOTE: 
     *   Setting "Default" to base class helps us when working migration commands on Package Manager Console.
     *   But it may cause problems when working Migrate.exe of EF. If you will apply migrations on command line, do not
     *   pass connection string name to base classes. ABP works either way.
     */
    public AbpProjectNameDbContext()
        : base("Default")
    {

    }

    /* NOTE:
     *   This constructor is used by ABP to pass connection string defined in AbpProjectNameDataModule.PreInitialize.
     *   Notice that, actually you will not directly create an instance of AbpProjectNameDbContext since ABP automatically handles it.
     */
    public AbpProjectNameDbContext(string nameOrConnectionString)
        : base(nameOrConnectionString)
    {

    }

    //This constructor is used in tests
    public AbpProjectNameDbContext(DbConnection existingConnection)
     : base(existingConnection, false)
    {

    }

    public AbpProjectNameDbContext(DbConnection existingConnection, bool contextOwnsConnection)
     : base(existingConnection, contextOwnsConnection)
    {

    }
}

ABP uses the 2nd constructor (AbpProjectNameDbContext(string nameOrConnectionString)) here, as stated in the comment. Default constructor is only used while you are working with PMC, like executing update-database.

So, you should add a constructor like

public SaaSZeroDbContext(string nameOrConnectionString)
   : base(GetConnectionStringFromUrl())
{

}

This ctor will be called by ABP, but you ignore nameOrConnectionString and generating it dynamically. That's OK. But there is still a problem. When you use PMC commands (like update-database), HttpContext.Current will be null. Because this execution will not be in a HTTP Context. Also, in unit tests there is no HttpContext.Current and so on.

So, your GetConnectionStringFromUrl() method should be able to work even if HttpContext.Current is null. If it's null, you can return a default connection string, for example.

Although this approach can work, I don't suggest it. ASP.NET Boilerplate already provides a way of dynamically determining the connection string on the runtime.

  1. Create a new class that implements IConnectionStringResolver. You can derive your class from DefaultConnectionStringResolver and override GetNameOrConnectionString method too. Thus you can call base.GetNameOrConnectionString if HttpContext.Current is null and your code fallbacks to default implementation.

  2. Replace IConnectionStringResolver with your own implementation as documented here.

Sign up to request clarification or add additional context in comments.

Comments

0

HttpContext.Current and other request specific information is obviously available only when code handles request.

Most likely you are initializing your class at a time when there is no request processed even if it clearly needs to be per-request as it uses request's information to select connection string. Reasonable fix would be to align lifetime of SaaSZeroDbContext to controller's lifetime (i.e. making SaaSZeroDbContext per-request object injected by a DI framework).

There is another option when context is not available even if you believe you are handling request - using ConfirgureAwiat(false) will lose access to request's objects after the call, but it does not look you got that far yet.

Security note: make sure user can't actually provide connections string via url, but rather gives your code hint on what existing connection string to use. Allowing user to control connection string (like ?dbname=FooBar) is huge security issue (read on "SQL injection" for more info).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.