实例化 EF Core 数据库上下文



我在使用 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>();
...

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

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

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

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

从其他帖子中,我注意到许多人说我不需要最后三行,但删除它们给了我例外,告诉我其他对象不能再由 DI 构造。

所以我很困惑...

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

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

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

尝试激活"其他类型"时无法解析类型"IMyContext"的服务。

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

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

您的上下文不需要接口。我看到人们一直在这样做,这简直令人麻木。绝对没有点。在您的上下文中唯一应该公开访问的内容应该是您的DbSet属性和内置方法,如AddSaveChangesAsync等。如果你想或需要一般地使用DbSet,有Set<T>,所以你的上下文类可以直接注入。此外,您不会对此上下文进行多次实现。它是数据库的表示形式,因此,它是:一个永远的实现。

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

更新

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

public interface IService
{
void DoSomething();
}

然后,您将为每个提供商创建实现,例如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 纠缠在一起。

最新更新