C# 在DbContext类中使用HttpContext

C# 在DbContext类中使用HttpContext,c#,asp.net-mvc,entity-framework,aspnetboilerplate,C#,Asp.net Mvc,Entity Framework,Aspnetboilerplate,我正试图从请求url获取数据库名称,因为我有多个数据库 因此,我有x1.com、x2.com和x3.com,我希望DbContext在运行时从url生成连接字符串 我所做的就是 public SaaSZeroDbContext() : base(GetConnectionStringFromUrl()) { } //function to get the connection string private static string Get

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

因此,我有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为空,为什么HttpContext.Current为空?

HttpContext.Current和其他特定于请求的信息显然只有在代码处理请求时才可用

很可能是在没有处理请求的时候初始化类,即使它显然需要按请求进行初始化,因为它使用请求的信息来选择连接字符串。合理的解决方案是将
SaaSZeroDbContext
的生存期与控制器的生存期对齐(即,对DI框架注入的每个请求对象进行
SaaSZeroDbContext

当上下文不可用时,即使您认为您正在处理请求,也有另一种选择-使用
confirguerawiat(false)
将在调用后失去对请求对象的访问权限,但看起来您还没有做到这一点


安全注意事项:确保用户不能通过url实际提供连接字符串,而是向您的代码提示要使用的现有连接字符串。允许用户控制连接字符串(如
?dbname=FooBar
)是一个巨大的安全问题(请阅读“SQL注入”了解更多信息)。

您的方法在设计上是不正确的。我会解释原因

这是模板附带的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将调用此ctor,但忽略nameOrConnectionString并动态生成它。没关系。但仍然存在一个问题。使用PMC命令(如更新数据库)时,HttpContext.Current将为null。因为此执行将不在HTTP上下文中。此外,在单元测试中没有HttpContext.Current等等

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

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

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

  • 将IConnectionStringResolver替换为您自己的实现


  • HttpContext
    作为参数从控制器传递到您的方法一层中的控制器和GetConnectionStringFromUrl在另一层中,我正在使用aspnetboilerplate模板,如何做到这一点?我键入并删除了一个长答案。简短的版本是,我不建议从URL确定数据库。这是应用程序的一个完全独立的层。从URL有效地获取它可以让用户决定应该使用哪个数据库。即使你有足够的安全性来防止误用,这也不是一个推荐的模式。一个请求进入一个数据库与另一个请求进入另一个数据库的区别是什么?我将从这里开始,看看您的控制器如何将正确的信息传递到下一层。@ScottHannen我认为OP不允许url控制DB连接字符串,只需从已知的一次列表中选择一次,它实际上没有您所说的任何安全问题。我关心的主要不是安全性。更重要的是,数据库是一个不同的层。在浏览器甚至控制器级别,没有(或不应该)意识到不同的数据库甚至任何数据库都存在。都是抽象的。
    public SaaSZeroDbContext(string nameOrConnectionString)
       : base(GetConnectionStringFromUrl())
    {
    
    }