使用实现2个接口的EF DbContext进行依赖项注入
本文关键字:依赖 注入 DbContext EF 实现 2个 接口 | 更新日期: 2023-09-27 18:21:52
给定一个DbContext,它实现了两个类似的接口:
public interface IQueryEntities
{
IQueryable<User> Users { get; }
IQueryable<Computer> Computers { get; }
// other IQueryable<T> get properties
}
public interface IUnitOfWork
{
int SaveChanges();
}
public class MyContext : DbContext, IQueryEntities, IUnitOfWork
{
// implement interfaces using EF
}
第一个问题,将DbContext(IDbSet)的查询方面与命令方面(SaveChanges)分离是不是一个坏主意?我正在探索对上述内容的重构,因为在很多情况下,我们只需要查询数据,而不需要保存任何内容。
我遇到的问题涉及统一DI,它目前为IUnitOfWork接口注入MyDbContext,使用每http上下文生存期一个singleton。我不知道如何为IQueryEntities接口设置注入,以便它重用可能已经针对IUnitOfWork接口注入的现有DbContext实例。反之亦然。这可能吗?
以下是当前的生存期管理器,它在相同的http上下文中重用以前注入的IUnitOfWork实例:
public class UnityHttpContextLifetimeManager : LifetimeManager
{
private const string KeyFormat = "SingletonPerCallContext_{0}";
private readonly string _key;
public UnityHttpContextLifetimeManager()
{
_key = string.Format(KeyFormat, Guid.NewGuid());
}
public override object GetValue()
{
return HttpContext.Current.Items[_key];
}
public override void SetValue(object newValue)
{
HttpContext.Current.Items[_key] = newValue;
}
public override void RemoveValue()
{
HttpContext.Current.Items.Remove(_key);
}
}
顺便说一句,如果有办法做到这一点,我更愿意在unity-web.config部分中完成,而不是编译的c#bootstrapper。
更新
在onof的帮助下,我能够做到这一点,但我的配置看起来与他建议的不同。我做错什么了吗?当我不给每个接口提供生存期管理器时,一个HttpContext最终会有多个DbContext实例。只有当我给所有3个生存期管理器时,它才会在两个接口的单个请求中重用相同的DbContext实例。这个配置有问题吗?
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<namespace name="MyApp.MyNameSpace" />
<assembly name="MyApp" />
<alias alias="singleton-per-http-context"
type="MyApp.MyNameSpace.UnityHttpContextLifetimeManager, MyApp" />
<container>
<register type="MyContext">
<lifetime type="singleton-per-http-context" />
</register>
<register type="IUnitOfWork" mapTo="MyContext">
<lifetime type="singleton-per-http-context" />
</register>
<register type="IQueryEntities" mapTo="MyContext">
<lifetime type="singleton-per-http-context" />
</register>
...
</container>
将DbContext的查询方面分开是个坏主意吗(IDbSets)从命令方面(SaveChanges)?
我认为这是一个好主意,因为接口分离原则规定,每个客户端应该只看到它完成工作所需的接口。
要注册,我会做:
container.RegisterType<MyContext>(new UnityHttpContextLifetimeManager());
container.RegisterType<IQueryEntities, MyContext>();
container.RegisterType<IUnitOfWork, MyContext>();
AFAIK一旦创建了对象,这是共享同一实例的唯一方法。
要在设计时(在web.config中)做到这一点,很简单:
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<namespace name="MyApp.MyNameSpace" />
<assembly name="MyApp" />
<container>
<register type="MyContext" >
<lifetime type="UnityHttpContextLifetimeManager" />
</register>
<register type="IQueryEntities" mapTo="MyContext" />
<register type="IUnitOfWork" mapTo="MyContext" />
</container>
您需要将一个接口注册为singleton,另一个将自动跟随。
container.RegisterType<IQueryEntities, MyContext>(new UnityHttpContextLifetimeManager());
container.RegisterType<IUnitOfWork, MyContext>();
假设您的LifetimeManager工作正常,则会将MyContext
的实例的生存期限定为HttpContext
,并且来自IUnitOfWork
的映射将重用同一实例,因为映射的目标是相同的。