在我的DbContext类中使用HttpContext

时间:2017-12-20 23:07:03

标签: c# asp.net-mvc entity-framework aspnetboilerplate

我试图从请求网址获取数据库名称,因为我有多个数据库

所以,我有x1.com,x2.com和x3.com我想让DbContext从运行时的url生成连接字符串

我做的是那个

  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;
    }

但是我收到错误HttpContext.Current为null,为什么HttpContext.Current为null?

2 个答案:

答案 0 :(得分:1)

你的方法不符合设计。我会解释原因。

这是模板附带的DbContext:

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在这里使用第二个构造函数(AbpProjectNameDbContext(string nameOrConnectionString)),如评论中所述。默认构造函数仅在您使用PMC时使用,例如执行update-database。

所以,你应该添加一个像

这样的构造函数
public SaaSZeroDbContext(string nameOrConnectionString)
   : base(GetConnectionStringFromUrl())
{

}

此ctor将由ABP调用,但您忽略nameOrConnectionString并动态生成它。没关系。但仍有问题。当您使用PMC命令(如update-database)时,HttpContext.Current将为null。因为此执行不在HTTP上下文中。另外,在单元测试中没有HttpContext.Current等等。

因此,即使HttpContext.Current为null,您的GetConnectionStringFromUrl()方法也应该能够工作。如果它为null,则可以返回默认连接字符串,例如。

虽然这种方法可行,但我不建议这样做。 ASP.NET Boilerplate已经提供了一种动态确定运行时连接字符串的方法。

  1. 创建一个实现IConnectionStringResolver的新类。您可以从DefaultConnectionStringResolver派生您的类,也可以重写GetNameOrConnectionString方法。因此,如果HttpContext.Current为null并且您的代码回退到默认实现,则可以调用base.GetNameOrConnectionString。

  2. 将IConnectionStringResolver替换为您自己的实现documented here

答案 1 :(得分:0)

仅当代码处理请求时,

HttpContext.Current和其他请求特定信息显然是可用的。

您很可能在没有请求处理的情况下初始化您的类,即使它显然需要按请求处理,因为它使用请求的信息来选择连接字符串。合理的解决方法是将SaaSZeroDbContext的生命周期与控制器的生命周期保持一致(即,通过DI框架注入每个请求对象SaaSZeroDbContext)。

当上下文不可用时,即使您认为自己正在处理请求,也有另一种选择 - 使用ConfirgureAwiat(false)会在调用后失去对请求对象的访问权限,但它看起来还不是那么远。

安全说明:确保用户无法通过url实际提供连接字符串,而是提供代码提示要使用的现有连接字符串。允许用户控制连接字符串(如?dbname=FooBar)是一个巨大的安全问题(有关更多信息请参阅“SQL注入”)。