拥有多个实体上下文
Having more than one entity context
假设我已经扩展了 identity framework dbContext
来构建我自己的,并且我有我们经过身份验证的控制器,它被注入 dbContext
并获取与当前的 ApplicationUser
、entity framework 将关联两个实体,由于循环引用而导致服务器错误。
我们不想序列化循环引用。
所以我们在实例化一个新的dbcontext
的方法中创建一个新的dbContext
并查询不相关的实体,这将起作用。但是这是不可测试的,我们不希望我们的控制器严格依赖dbContext
,我们希望它被注入。
所以我们在构造函数中添加第二个参数,不幸的是这会使系统注入相同的dbContext
两次,不是那么有用。
我们试图创建一个继承自dbContext
的classfakeDbContext
并添加服务并使用它,但现在我们有两个dbcontext,谁可能会产生迁移、配置和错误...
在新的 MVC6 中执行此操作的正确方法是什么?
编辑...
我发现如果我的控制器需要 IEnumerable<dbContext>
,我会将所有对象注册为该类型的服务,所以只需将 startup.cs[=69 中的部分加倍=] 我们在服务注册区添加 dbContext
的地方我得到了两个...
这里的缺点是我不知道哪个是virgin,看起来是按照注册的顺序,但我不知道,如果这个会改变。
编辑 2 ...
我创建了一个 TransientDbService
class,它只有一个采用 IserviceProvider
的工厂方法,它使用它来获取构建 dbContext
的选项,然后暴露它。我已将其注册为瞬态,然后在控制器中我需要此服务类型。
这里的缺点是如果我需要第三个 dbContext
我应该写更多的代码,更多的代码意味着错误和维护它。
编辑 3 ...
根本没有两个 dbContext。以下设置允许我没有任何关系。
Database.ChangeTracker.QueryTrackingBehavior = Microsoft.Data.Entity.QueryTrackingBehavior.NoTracking;
这里的缺点是我不能使用我的模型图,使一切变得更加复杂...
编辑 4 ...
您认为在某些情况下没有跟踪查询会有帮助是正确的,但在其他时候您需要创建多个 DbContext 实例。
您通常在启动时使用 AddDbContext<TContext>()
方法来确保根据请求创建上下文类型的实例,并在其上设置正确的 DbContextOptions
和服务提供商。当您需要偏离此模式时,您有几个选择,例如:
在派生的 DbContext class 中包含一个构造函数,该构造函数采用 IServiceProvider
并将其传递给基本构造函数。确保您的控制器占用 IServiceProvider
。执行此操作后,您应该能够使用以下内容手动创建 DbContext
:
using(var context1 = new MyDbContext(serviceProvider),
var context2 = new MyDbContext(serviceProvider))
{ ...
为了避免必须更改派生的 DbContext
类型的构造函数签名,您可以利用 DbContextActivator
class(这是我们的内部命名空间) , 例如:
using(var context1 = DbContextActivator.CreateInstance<MyDbContext>(serviceProvider),
var context2 = DbContextActivator.CreateInstance<MyDbContext>(serviceProvider)
{...
注意:如果您在启动时仍在使用 AddDbContext<MyDbContext>(options => ...)
,它应该会自动从服务提供商处提取这些选项。但是您也可以选择将 DbContextOptions
作为参数包含在构造函数中,或者为此覆盖 OnConfiguring()
方法。
我给你的例子是在 using 块中创建两个单独的 DbContexts
,但你也应该能够将它们与常规的 "per-request" DbContext 混合,你将在控制器的构造函数中注入.
除了这些当前可用的选项之外,我还创建了一个新问题来跟踪有关如何在同一请求中创建相同 DbContext
类型的多个实例的其他可能的改进:
假设我已经扩展了 identity framework dbContext
来构建我自己的,并且我有我们经过身份验证的控制器,它被注入 dbContext
并获取与当前的 ApplicationUser
、entity framework 将关联两个实体,由于循环引用而导致服务器错误。
我们不想序列化循环引用。
所以我们在实例化一个新的dbcontext
的方法中创建一个新的dbContext
并查询不相关的实体,这将起作用。但是这是不可测试的,我们不希望我们的控制器严格依赖dbContext
,我们希望它被注入。
所以我们在构造函数中添加第二个参数,不幸的是这会使系统注入相同的dbContext
两次,不是那么有用。
我们试图创建一个继承自dbContext
的classfakeDbContext
并添加服务并使用它,但现在我们有两个dbcontext,谁可能会产生迁移、配置和错误...
在新的 MVC6 中执行此操作的正确方法是什么?
编辑...
我发现如果我的控制器需要 IEnumerable<dbContext>
,我会将所有对象注册为该类型的服务,所以只需将 startup.cs[=69 中的部分加倍=] 我们在服务注册区添加 dbContext
的地方我得到了两个...
这里的缺点是我不知道哪个是virgin,看起来是按照注册的顺序,但我不知道,如果这个会改变。
编辑 2 ...
我创建了一个 TransientDbService
class,它只有一个采用 IserviceProvider
的工厂方法,它使用它来获取构建 dbContext
的选项,然后暴露它。我已将其注册为瞬态,然后在控制器中我需要此服务类型。
这里的缺点是如果我需要第三个 dbContext
我应该写更多的代码,更多的代码意味着错误和维护它。
编辑 3 ...
根本没有两个 dbContext。以下设置允许我没有任何关系。
Database.ChangeTracker.QueryTrackingBehavior = Microsoft.Data.Entity.QueryTrackingBehavior.NoTracking;
这里的缺点是我不能使用我的模型图,使一切变得更加复杂...
编辑 4 ...
您认为在某些情况下没有跟踪查询会有帮助是正确的,但在其他时候您需要创建多个 DbContext 实例。
您通常在启动时使用 AddDbContext<TContext>()
方法来确保根据请求创建上下文类型的实例,并在其上设置正确的 DbContextOptions
和服务提供商。当您需要偏离此模式时,您有几个选择,例如:
在派生的 DbContext class 中包含一个构造函数,该构造函数采用
IServiceProvider
并将其传递给基本构造函数。确保您的控制器占用IServiceProvider
。执行此操作后,您应该能够使用以下内容手动创建DbContext
:using(var context1 = new MyDbContext(serviceProvider), var context2 = new MyDbContext(serviceProvider)) { ...
为了避免必须更改派生的
DbContext
类型的构造函数签名,您可以利用DbContextActivator
class(这是我们的内部命名空间) , 例如:using(var context1 = DbContextActivator.CreateInstance<MyDbContext>(serviceProvider), var context2 = DbContextActivator.CreateInstance<MyDbContext>(serviceProvider) {...
注意:如果您在启动时仍在使用 AddDbContext<MyDbContext>(options => ...)
,它应该会自动从服务提供商处提取这些选项。但是您也可以选择将 DbContextOptions
作为参数包含在构造函数中,或者为此覆盖 OnConfiguring()
方法。
我给你的例子是在 using 块中创建两个单独的 DbContexts
,但你也应该能够将它们与常规的 "per-request" DbContext 混合,你将在控制器的构造函数中注入.
除了这些当前可用的选项之外,我还创建了一个新问题来跟踪有关如何在同一请求中创建相同 DbContext
类型的多个实例的其他可能的改进: