【问题标题】:Configure connection string from controller (ASP.NET Core MVC 2.1)从控制器配置连接字符串 (ASP.NET Core MVC 2.1)
【发布时间】:2019-02-02 06:59:44
【问题描述】:

我想从控制器配置连接字符串,但我没有找到任何功能可以做到这一点。

我有:

public class tstController : Controller
{
    private readonly contextTst _context;

    public tstController(contextTst context)
    {
        _context = context;
        _context.Database.**????**
    }
}

有可能吗?

非常感谢!

【问题讨论】:

  • 为什么来自控制器?您必须在启动时注册DbContext
  • 我想从导航器中写入连接字符串。
  • 不!你不能!这违背了 ASP.NET Core 的设计。

标签: c# asp.net-core asp.net-core-mvc


【解决方案1】:

鉴于您想在构造函数中设置一个替代连接字符串,建议它是一个已知值。

要解决的问题是如何使用 DI 做到这一点。第一个提示是脚手架上下文时生成的代码:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    if (!optionsBuilder.IsConfigured)
    {
//#warning To protect potentially sensitive information in your
// connection string, you should move it out of source code.
// See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.
        optionsBuilder.UseSqlServer("Server=.\\SQLEXPRESS;Database=MyDb;Trusted_Connection=True;");
    }
}

这意味着您可以使用默认配置 (optionsBuilder.IsConfigured),在启动时设置值。但也可以在构造上使用替代方案。

代码将如下所示:

public partial class MyContext : DbContext
{
    private readonly string _connectionString;

    public MyContext(DbContextOptions<MyContext> options)
        : base(options)
    {
    }

    public MyContext(IOptions<DbConnectionInfo> dbConnectionInfo)
    {
        _connectionString = dbConnectionInfo.Value.MyContext;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseSqlServer(_connectionString);
        }
    }
}

帮助类如下所示:

public class DbConnectionInfo
{
    public string MyContext { get; set; }
}

示例 appsettings.json:

"ConnectionStrings": {
    "MyContext": "Server=.\\SQLEXPRESS;Database=MyDb;Trusted_Connection=True;"
  },

并在启动时注册:

services.Configure<DbConnectionInfo>(settings => configuration.GetSection("ConnectionStrings").Bind(settings));
services.AddScoped<MyContext>();

如果您不想从配置中读取连接字符串,而是根据中间件(例如每个租户)进行设置,那么您可以使用相同的方法。 只需在构造上下文之前更新值即可。


更新:

使用依赖注入,您无需自己构造对象,而是将注册的对象/服务作为参数传递。 DI 将确定需要以什么顺序创建哪些对象。同样的方式,对象在使用后会被 DI 处理掉。

控制器“知道”上下文的事实是因为 DI 自动将其添加为参数。上下文“知道” DbConnectionInfo 的事实是因为它已注册到 DI。

如果您想更改 DbConnectionInfo,那么您需要以适当的方式添加它。在您的情况下,您可以执行以下操作:

// Added as part of the example
services.AddHttpContextAccessor();
// Replace registration with this line:
services.AddScoped<DbConnectionInfo>();
// Register the DbContext
services.AddScoped<MyContext>();

类的替代版本在哪里:

public class DbConnectionInfo
{
    public string MyContext { get; set; }

    // Example injecting IHttpContextAccessor
    // On creating this class DI will inject 
    // the HttpContextAccessor as parameter
    public DbConnectionInfo(IHttpContextAccessor httpContextAccessor)
    {
        // Access the current request
        var request = httpContextAccessor.HttpContext.Request;

        // Access the current user (if authenticated)
        var user = httpContextAccessor.HttpContext.User;

        // Now you could get a value from a header, claim,
        // querystring or path and use that to set the value:

        MyContext = "";
    }
 }

并且在 DbContext 中做了一个小改动,在这种情况下我们不使用 IOptions:

public partial class MyContext : DbContext
{
    private readonly string _connectionString;

    public MyContext(DbConnectionInfo dbConnectionInfo)
    {
        _connectionString = dbConnectionInfo.MyContext;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder     optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseSqlServer(_connectionString);
        }
    }
}

现在每个请求的值都会在创建 MyContext 之前设置。

【讨论】:

  • 要在构造上下文之前更新值,从我正在尝试的控制器:DbConnectionInfo conInfo = new DbConnectionInfo(); conInfo.MyContext = ; _context = new contextTst(conInfo);但是我得到 conInfo 的构造函数的错误:“Argument 1: Unable to convert...” 非常感谢,
  • @user3682831 您需要使用依赖注入来创建新对象。不要在此处使用 new 关键字。我已经更新了答案。
  • 谢谢,但构造函数“MyContext(IOptions dbConnectionInfo)”给了我错误:“DbConnectionInfo 必须是具有公共无参数构造函数的非抽象类型......”,所以我无法编译。
  • @user3682831 我已经更新了答案。我忘记更新 MyContext 类。答案的第一部分使用了 IOptions,因为 services.Configure 使用了 IOptions。但在第二部分中,DbConnectionInfo 是在没有 IOptions 的情况下注册的。
【解决方案2】:

您应该仅在启动期间注册 DbContext。

只有你应该指定连接字符串。

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnectionString")));

    services.AddMvc();
}

如果你是using dependency injection (from MSDN),这一点非常重要:

实体框架上下文 应使用作用域生命周期将实体框架上下文添加到服务容器中。这是 调用 AddDbContext 方法时自动处理 注册数据库上下文。使用数据库的服务 上下文也应该使用作用域生命周期。

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-11
    • 1970-01-01
    • 2018-05-11
    • 1970-01-01
    • 2019-09-17
    • 1970-01-01
    • 1970-01-01
    • 2018-09-07
    相关资源
    最近更新 更多