ASP.NET Core 2.0:为什么 services.AddDbContext 不接受接口?
ASP.NET Core 2.0: Why doesn't services.AddDbContext accept an interface?
我的理解是,使用依赖项注入的一个关键优势是您不会紧密绑定到类型,因此您以后可以用比其他方式更少的工作量换出您的体系结构的各个部分。如果是这样,为什么我会看到这样的代码:
// 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 class 类型,而不是 IAppDbContext 接口。但为什么?这是不是太抽象了?如果是这样,为什么还要用接口向容器注册任何东西?
So, when I want to access this type, I place an AppDbContext class type in my constructor, not an IAppDbContext interface. But why? Is this too much abstraction?
泛型类型约束表示可以注册任何DbContext
。 ApplicationDbContext
是该抽象的具体实现。添加另一个抽象并不能完成任何事情。
And if so, why bother registering anything to the container with interfaces?
我们不向 DI 容器注册 interfaces,我们注册 abstractions。抽象包括抽象 类 和接口,两者都用于我们在使用依赖注入时所追求的解耦目的。
在这种特殊情况下,还有其他目标。
- 我们想要唯一标识
DbContext
的特定实例。这使得在一个应用程序中使用多个数据库上下文变得很容易。
DbContext
需要在正确的生命周期内注册,以便在每次请求后对其进行处理,而将其注册到 DI 容器是实现这一目标的最简单方法。
DbContext
也必须是请求的单个实例。打开多个实例可能会导致数据库同步问题甚至运行时错误。向 DI 容器注册是获取每个请求生命周期的便捷方式。
也就是说,这是一个特例,绝不是完成工作的唯一方法 - 如果您热衷于确保将其注册为抽象类型而不是具体类型,您可以随时使用 这样做(尽管我认为如果您实际上不需要在运行时在数据库之间切换,那它就太过分了)。
对于应用程序的其余部分,确实最好的做法是注册为抽象类型而不是具体类型,以便轻松 swapping/mocking 依赖。
要解决此问题,请使用具有 2 个通用类型参数的重载之一,它允许您指定要注册的服务 interface/class 以及实现它的 DbContext 派生 class .
services.AddDbContext<IMyDbContext, MyDbContext>(options =>
options.UseSqlServer(...));
我的理解是,使用依赖项注入的一个关键优势是您不会紧密绑定到类型,因此您以后可以用比其他方式更少的工作量换出您的体系结构的各个部分。如果是这样,为什么我会看到这样的代码:
// 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 class 类型,而不是 IAppDbContext 接口。但为什么?这是不是太抽象了?如果是这样,为什么还要用接口向容器注册任何东西?
So, when I want to access this type, I place an AppDbContext class type in my constructor, not an IAppDbContext interface. But why? Is this too much abstraction?
泛型类型约束表示可以注册任何DbContext
。 ApplicationDbContext
是该抽象的具体实现。添加另一个抽象并不能完成任何事情。
And if so, why bother registering anything to the container with interfaces?
我们不向 DI 容器注册 interfaces,我们注册 abstractions。抽象包括抽象 类 和接口,两者都用于我们在使用依赖注入时所追求的解耦目的。
在这种特殊情况下,还有其他目标。
- 我们想要唯一标识
DbContext
的特定实例。这使得在一个应用程序中使用多个数据库上下文变得很容易。 DbContext
需要在正确的生命周期内注册,以便在每次请求后对其进行处理,而将其注册到 DI 容器是实现这一目标的最简单方法。DbContext
也必须是请求的单个实例。打开多个实例可能会导致数据库同步问题甚至运行时错误。向 DI 容器注册是获取每个请求生命周期的便捷方式。
也就是说,这是一个特例,绝不是完成工作的唯一方法 - 如果您热衷于确保将其注册为抽象类型而不是具体类型,您可以随时使用
对于应用程序的其余部分,确实最好的做法是注册为抽象类型而不是具体类型,以便轻松 swapping/mocking 依赖。
要解决此问题,请使用具有 2 个通用类型参数的重载之一,它允许您指定要注册的服务 interface/class 以及实现它的 DbContext 派生 class .
services.AddDbContext<IMyDbContext, MyDbContext>(options =>
options.UseSqlServer(...));