我的理解是,使用依赖注入的一个关键优势是您不会紧密绑定到类型,因此您可以稍后用较少的工作来交换您的架构的各个部分。如果是这样的话,为什么我看到这样的代码:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
...
}
我很好奇的方法是:
public static IServiceCollection AddDbContext<TContext>([NotNullAttribute] this IServiceCollection serviceCollection, [CanBeNullAttribute] Action<DbContextOptionsBuilder> optionsAction = null, ServiceLifetime contextLifetime = ServiceLifetime.Scoped, ServiceLifetime optionsLifetime = ServiceLifetime.Scoped) where TContext : DbContext;
因此,当我想访问此类型时,我将AppDbContext类类型放在构造函数中,而不是IAppDbContext接口。但为什么?这太抽象了吗?如果是这样,为什么还要用接口向容器注册任何内容呢?
答案 0 :(得分:2)
因此,当我想访问此类型时,我将AppDbContext类类型放在构造函数中,而不是IAppDbContext接口。但为什么?这太抽象了吗?
泛型类型约束表示可以注册任何DbContext
。 ApplicationDbContext
是该抽象的具体实现。添加另一个抽象并不能完成任何事情。
如果是这样,为什么还要用接口向容器注册任何内容?
我们没有在DI容器中注册 interfaces ,我们注册了抽象。抽象包括抽象类和接口,并且用于在使用依赖注入时解耦的目的。
在这种特殊情况下,还有其他目标。
DbContext
的特定实例。这使得在应用程序中使用多个数据库上下文变得容易。DbContext
需要在正确的生命周期内注册,以便在每次请求后进行处理,并将其注册到DI容器是最简单的方法。DbContext
也必须是请求的单个实例。打开多个实例可能会导致数据库同步问题甚至运行时错误。注册DI容器是获取每个请求生命周期的便捷方式。那就是说,这是一个特例,绝不是完成工作的唯一方法 - 如果你对确保它被注册为抽象而不是具体类型的狂热,你可以随时使用{ {3}}这样做(尽管如果你实际上不需要在运行时间在数据库之间切换,我认为它已经过顶了。)
对于应用程序的其余部分,最佳做法是注册为抽象而不是具体类型,以便轻松交换/模拟依赖项。
答案 1 :(得分:0)
要解决此问题,请使用具有 2 个泛型类型参数的重载之一,这允许您指定要注册的服务接口/类以及实现它的 DbContext 派生类。
services.AddDbContext<IMyDbContext, MyDbContext>(options =>
options.UseSqlServer(...));