我在使用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?
答案 0 :(得分:0)
所以我想出了如果我修改工厂代码,则可以在数据库“设置”场景中很好地处理构造。
然后,这使我只能在需要的上下文中使用一个CTOR,但是最终结果是我仍然需要这些额外的行,否则它会因DI异常而失败...
尝试激活“其他类型”时,无法解析“ IMyContext”类型的服务。
然后单击它……我从不要求“ MyDbContext”,我总是要求“ IMyDbContext”……额外的行是将容器中的接口调用映射到强类型上下文而不是“将上下文重新添加到”容器中。
答案 1 :(得分:0)
您不应该不要根据范围分别添加上下文。那是问题的主要根源。当您使用AddDbContext
时,它将注册您的DbContext
派生的类。如果继续请求一个接口,它不知道如何满足该接口。它只知道如何满足班级要求。这就是为什么您会获得异常,这就是为什么您感到需要添加单独的接口注册。但是,当您执行那个时,您正在请求上下文类的另一个实例,因为它是完全不同的注册。
您不需要上下文界面。我看到人们一直在这样做,这只是在麻木。绝对为零。在您的上下文中应该可以公开访问的唯一内容应该是DbSet
属性和内置方法,例如Add
,SaveChangesAsync
等。如果您想或需要与DbSet
,有Set<T>
,因此您的上下文类可以直接注入。此外,这不像您要对此上下文进行多种实现。它是数据库的表示形式,因此,它是:一直以来都是一种实现。
此外,除了DbContextOptions
之外,您不应在上下文中注入其他任何内容。如果您需要服务,DbContext
有一个内部服务提供商,那就是您应该使用的,即this.GetService<IAuthInfo>
。
更新
基于评论主题,我认为我的建议不清楚。您应该有类似的内容:
public interface IService
{
void DoSomething();
}
然后,您将为每个提供商创建 this 的实现,例如CosmosDbService
,RavenDbService
,EFService
等。然后在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纠缠在一起:作为一个简单的工作单元。