【问题标题】:How do I get an IConfiguration instance in my DbContext?如何在我的 DbContext 中获取 IConfiguration 实例?
【发布时间】:2020-08-27 16:20:32
【问题描述】:

我见过很多 tutorials 解释 .NET core + EF 配置,但没有一个解释如何在 DbContext 中获取 Configuration 实例。

我需要这个的原因是为了获取数据库连接字符串:

namespace Models
{
    public class BlogContext : DbContext
    {

        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder options)
            => options.UseSqlite(Configuration.GetConnectionString("BlogDb"));

    }
}

我的连接字符串在appsettings.json 文件中:

{
    "ConnectionStrings": {
        "BlogDB": "Data Source=blogging.db"
    }
}

大多数教程在 db 上下文中显示IConfiguration 的实例。我怀疑它是通过构造函数发生的:

private readonly IConfiguration Configuration;
public BlogContext(IConfiguration config) {
    Configuration = config;
}

但是,这实际上是如何注入的?

这就是我使用上下文的方式:

public class Seed
    {
        public static void SeedDatabase()
        {
            using var db = new BlogContext();
            ... stuff ...
        }
    }

我如何真正从设置文件中获取连接字符串?做一些非常简单的事情似乎很混乱,需要做很多工作......

更新

我像这样使用Seed

public class Program
    {
        public static void Main(string[] args)
        {
            Seed.SeedDatabase();
        }
    }

我如何真正将DbContext 的实例放入SeedDatabase

【问题讨论】:

    标签: c# entity-framework .net-core


    【解决方案1】:

    根据上面的代码,看起来在依赖注入和静态代码上可能存在一些混合和匹配,这让你很头疼。您的中间示例期望配置通过构造函数注入进入,但随后您在无法使用它的静态方法中新建了一个实例。

    理想情况下,在您的服务注册期间,您应该执行以下操作:

    services.AddDbContext<BlogContext>(options => 
        options.UseSqlite(Configuration.GetConnectionString("BlogDb")));
    

    这是将 DB 上下文注册到服务容器中,并设置了选项委托以使用来自 appsettings 的连接字符串,因此您的上下文将如下所示:

    public BlogContext(DbContextOptions<BlogContext> options)
       : base(options)
    {
    // ... 
    }
    

    而且您的上下文不再直接依赖 IConfiguration 对象。此处的 Microsoft 文档中有一个示例:

    https://docs.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext#using-dbcontext-with-dependency-injection

    不过,无论使用哪种路由,您都需要由 DI 容器注入 DbContext 以解析依赖关系树,因此尝试在静态方法中新建上下文是行不通的——您必须以某种方式获得那种依赖(因此你的问题)。如果我没看错的话,这里有一点 XY 问题,所以我会尝试涵盖两者。

    如果您确实需要维护所编写的代码,您可以将 IConfiguration 注入到 Seed 类(或您从中调用的任何其他类)中,然后通过方法注入将其传递给静态方法。 Seed 类本身(或任何其他实例化的类)必须注册到 DI 容器,以便将 IConfiguration 传递给它自己的构造函数,然后在您使用它的地方注入:

    public class Seed
    {
        private readonly IConfiguration _configuration;
    
        // The Seed instance itself could have the IConfiguration injected and be available to pass to the static method...
        public Seed(IConfiguration configuration)
        {
            _configuration = configuration;
        }
    
        // ... but either way, when you call this, you have to pass in the dependency yourself, either from this class's instance, or from another one
        public static void SeedDatabase(IConfiguration configuration)
        {
            using (var db = new BlogContext(configuration))
            {
                //...
            }
        } 
    

    }

    更实际的是,您似乎正在尝试为数据库播种(duh)。如果是这种情况,请查看 EF Core 的播种机制:https://docs.microsoft.com/en-us/ef/core/modeling/data-seeding

    更新 根据您发布的附加代码添加一些进一步的信息。在您的示例中,您正试图在程序的入口点点击您的静态种子函数。仍然很难从代码中判断您是否正在构建主机和服务容器,所以我现在假设不是。您可以尝试以下方法:

    using System;
    using System.IO;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.Configuration;
    
    public class Program
    {
        public static void Main(string[] args)
        {
            var configuration = new ConfigurationBuilder()
                .AddJsonFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json")) // Grabs the appsettings JSON as the configuration source
                .Build();
    
            var dbOptions = new DbContextOptionsBuilder<BlogContext>()
                .UseSqlite(configuration.GetConnectionString("BlogDb"));
    
            Seed.SeedDatabase(new BlogContext(dbOptions)); // Pass in your prepared DbContext via method injection
            //...
        }
    }
    

    EF Core 种子功能可能仍会帮助您获得更强大的功能。

    【讨论】:

    • 在注册期间配置连接字符串听起来像是要走的路。但是我仍然不知道IConfiguration 实例的来源。看起来services.AddDbContextStartup.cs 中。如何在其中获取 IConfig 的实例??
    • Startup.cs 是 ASP NET Core 默认为您启动的主机构建器进程的一部分。当您在程序入口点的Program.cs 期间执行CreateDefaultBuilder 步骤时,它会运行几个步骤,从几个不同的地方组装该配置,然后将其注入到 Startup 类中。这里有一些关于来源的更多细节:docs.microsoft.com/en-us/aspnet/core/fundamentals/host/…——这个行为可以自定义,但是你的 appsettings.json 是默认加载的
    • 我明白了。我如何才能真正将 DbContext 的实例放入依赖类?如果我要新建一个 Seed 的实例,我需要从 DI 寄存器中获取 DbContext,或者手动创建一个新实例并将其传入。我该怎么做?
    • 我根据您的更新在我的答案中添加了一些额外的内容。由于您在入口点显示调用此权限,因此主机构建器和服务注册代码都没有运行(假设您正在这样做),因此没有可从中获取或注入 DbContext 的容器。如果这是您需要运行它的地方,您可以通过 ConfigurationBuilder 启动一个新配置并获取您需要的内容。
    【解决方案2】:

    您也应该使用带有注入的 DbContext。请注意,任何要注入的东西都需要作为服务添加。您通常可以从Startup 类中添加一个类作为服务。对于 DbContext,您将在 ConfigureServices 方法中使用扩展方法 AddDbContext

    services.AddDbContext<BlogContext>();
    

    一旦你使用上面的这段代码,你就可以按照你尝试的方式注入IConfiguration

    但是这不是设置网络应用程序时的最佳做法。 IConfiguration 一般只用在启动类中。在这种情况下,您可以将参数传递给AddDbContext 函数以添加配置。

    services.AddDbContext<BloggingContext>(options => options.UseSqlite(Configuration.GetConnectionString("BlogDb"));
    

    如果你使用这个方法,你还应该记得使用下面的构造函数:

    public class BlogContext : DbContext
    {
        public BlogContext(DbContextOptions<BlogContext> options)
        :base(options)
        { }
    
        public DbSet<Blog> Blogs { get; set; }
    }
    

    有关此过程的完整文档,请查看此链接:https://docs.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext

    数据播种也内置在 EF Core 中。你可以检查这个: https://docs.microsoft.com/en-us/ef/core/modeling/data-seeding

    【讨论】:

    • 我如何真正让 DbContext 在某处使用?
    【解决方案3】:

    在你的种子类中的某个地方:

    var config = new ConfigurationBuilder()
                     .SetBasePath(Directory.GetCurrentDirectory())
                     .AddEnvironmentVariables()
                     .Build();
    var dbContext = new DbContext(config);
    

    【讨论】:

      猜你喜欢
      • 2018-06-09
      • 1970-01-01
      • 2019-09-30
      • 1970-01-01
      • 1970-01-01
      • 2022-10-07
      • 2018-11-20
      • 2021-12-04
      • 2021-11-01
      相关资源
      最近更新 更多