【问题标题】:Instantiating EF Core DbContexts实例化 EF Core DbContexts
【发布时间】:2019-11-21 04:16:12
【问题描述】:

我在使用 EF Core 时遇到了一个奇怪的问题,我不明白为什么......

public class Startup
{
    static Config Config;

    public Startup(IConfiguration configuration)
    {
        Config = new Config();
        configuration.Bind(Config);
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor();
        services.AddScoped(ctx => ctx.GetService<IHttpContextAccessor>()?.HttpContext);
        services.AddScoped(ctx => ctx.GetService<HttpContext>()?.Request);
        services.AddAuthInfo();
        services.AddSingleton(Config);

        services.AddDbContext<MembershipDataContext>(options => options.UseSqlServer(Config.Connections["Membership"]));
        services.AddDbContext<CoreDataContext>(options => options.UseSqlServer(Config.Connections["Core"]));
        services.AddDbContext<B2BDataContext>(options => options.UseSqlServer(Config.Connections["B2B"]));

        services.AddScoped<IMembershipDataContext, MembershipDataContext>();
        services.AddScoped<ICoreDataContext, CoreDataContext>();
        services.AddScoped<IB2BDataContext, B2BDataContext>();

        ...

...我从每个请求中提取自定义身份验证信息并将其注入到我的 DbContexts 中。

由于初始化过程,我必须有一个接受 DbContextOptions 的 CTOR,所以我简单地添加了第二个,希望它会调用正确的...

public EFDataContext(DbContextOptions options, IAuthInfo auth) : base(options) { AuthInfo = auth; }

public EFDataContext(DbContextOptions options) : base(options) { }

...在运行时,我看到两个 CTOR 在一个请求中多次被命中(不是我所期望的)。

从其他帖子中,我注意到很多人说我不需要最后三行,但删除它们会给我带来异常,告诉我其他对象不能再由 DI 构造。

所以我很困惑......

我如何让这个工作,以便我只能为每个请求构建 1 个实例,并且只在我这样做时使用最多的参数命中 CTOR?

【问题讨论】:

  • 您为什么要为您的上下文使用AddScopedAddDbContext 会将它们添加为范围服务。
  • 问题中已经解释过了
  • 嗯,我不确定你是否可以扩展 DbContext 构造函数,我没有尝试过,但我没有尝试过的原因是因为我认为你不应该将身份验证信息注入其中首先。
  • 这是一个自定义对象,用于帮助我确定数据库中包含事务信息的多租户上下文,基于过滤器被修改的上下文......这在 EF6 中完美运行......没有不过没关系,关键是 EF Contexts 首先应该能够接受其他参数作为 DI 标准前提的一部分。
  • @DavidG 我发现了... :)

标签: c# asp.net-core dependency-injection entity-framework-core


【解决方案1】:

所以我发现如果我修改工厂代码,我可以在数据库的“设置”场景中处理好构造。

这让我在上下文中只有一个 CTOR,这是我需要的,但最终结果是我仍然需要那些额外的行,否则它会因 DI 异常而失败......

在尝试激活“SomeOther type”时无法解析“IMyContext”类型的服务。

然后它点击了...我从不要求“MyDbContext”我总是要求“IMyDbContext”...额外的行是将容器中的接口调用映射到强类型上下文而不是“重新添加上下文”到容器中。

【讨论】:

    【解决方案2】:

    您不应该单独添加上下文作为作用域。这是你问题的主要根源。当您使用AddDbContext 时,它会注册您的DbContext 派生类。如果你继续请求一个接口,它不知道如何满足那个接口;它只知道如何满足班级。这就是你得到异常的原因,也是你觉得需要添加单独的接口注册的原因。但是,当您这样做时,您正在请求上下文类的不同实例,因为它是完全不同的注册。

    您的上下文不需要接口。我看到人们一直这样做,这只是令人麻木。绝对是零点。唯一应该在您的上下文中公开访问的应该是您的 DbSet 属性和内置方法,如 AddSaveChangesAsync 等。如果您想要或需要一般地使用 DbSets,这里有Set&lt;T&gt; 为此,您的上下文类可以直接注入。此外,您不会有此上下文的多个实现。它是您的数据库的一种表示,因此,它就是:永远的一种实现。

    此外,除了DbContextOptions,您不应该将任何内容注入到您的上下文中。如果您需要服务,DbContext 有一个内部服务提供者,这就是您应该使用的,即this.GetService&lt;IAuthInfo&gt;

    更新

    根据评论线程,我认为我并不清楚我的建议。你应该有类似的东西:

    public interface IService
    {
        void DoSomething();
    }
    

    然后,您将为每个提供程序创建 this 的实现,例如CosmosDbServiceRavenDbServiceEFService 等。然后在 EFService 中,您将注入上下文类,不是接口

    public class EFService : IService
    {
        private readonly MyDbContext _context;
    
        public EFService(MyDbContext context)
        {
            _context = context;
        }
    
        public void DoSomething()
        {
            // use _context
        }
    }
    

    这样,为了 EF,上下文只有存在。没有添加任何无关的东西,也没有业务逻辑存在。在代码中的其他任何地方,注入IService,然后通过 DI 容器插入适当的提供程序实现(EF、Cosmos、Raven 等)。您的所有业务逻辑都应该存在于服务类中,并且您的上下文不需要接口。

    这也可能意味着您在上下文中也不需要像 IAuthInfo 这样的无关服务,这意味着您可以避免使用内部服务提供商。您应该将 IAuthInfo 注入您的服务类。

    你现在所做的恰恰相反。通过使上下文实现一个与其目的无关的接口:作为一个简单的工作单元,您从根本上将您的业务逻辑与 EF 纠缠在一起。

    【讨论】:

    • 在我的堆栈中我确实需要接口......它可能不是由 EF 实现的,并且 EF 代码存在于与 Cosmos 代码或 raven 代码分离的业务逻辑的单独程序集中不使用EF。该接口是我对数据源的抽象,在这种情况下,EF 恰好“碰巧”实现了该数据源。
    • 关于我不知道的内部服务提供商的最后一点......我会修补的。
    • 我明白你的意思,但这仍然是错误的方法。您应该有一个在内部使用不同数据提供者的服务。那么你可以有一个接口为此。直接使用上下文作为实现要么会导致 1) 所有其他提供者都必须遵守 EF 约定,要么 2) 使你的上下文做得比它应该做的更多。
    • 我的接口公开了 IQueryable 用于获取和通用的 Add、Update 等方法......其中没有任何特定于 EF 的内容......在我的情况下,由于“业务原因”,我们经常发现实现各不相同,堆栈根据配置提取库......很难解释......但是想象一个代码堆栈,它作为初始化周期的一部分从它从各种 http 端点获取的组件构建,最终结果是一个应用程序。将 DI 容器放在上下文中……这就是反模式……blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern
    • 这里的另一个例子:stackoverflow.com/questions/22795459/…
    猜你喜欢
    • 2018-11-20
    • 2018-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-04
    • 2020-07-01
    相关资源
    最近更新 更多