使用EF DbContext实现2个接口的依赖注入

时间:2012-01-30 23:27:21

标签: c# dependency-injection entity-framework-4.1 unity-container dbcontext

给定一个实现2个接口的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
}

第一个问题,从命令方面(SaveChanges)中分离出DbContext(IDbSets)的查询方面是不是一个坏主意?我正在探索上面的重构,因为在很多情况下我们只需要查询数据而不保存任何内容。

我遇到的问题涉及统一DI,它目前使用IUnitOfWork接口的单身每个http上下文生命周期注入MyDbContext。我不确定如何为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);
    }
}

顺便说一句,如果有办法做到这一点,我宁愿在统一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>

2 个答案:

答案 0 :(得分:3)

  

分离出DbContext的查询方面是一个坏主意   (IDbSets)来自命令方面(SaveChanges)?

我认为这是一个好主意,因为Interface Segregation Principle,它指出每个客户端应该只看到它所需要的界面。

要注册,我会这样做:

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>

答案 1 :(得分:2)

您需要将一个界面注册为单身,另一个界面将自动跟随。

container.RegisterType<IQueryEntities, MyContext>(new UnityHttpContextLifetimeManager());
container.RegisterType<IUnitOfWork, MyContext>();

假设您的LifetimeManager正常工作,这会将MyContext实例的生命周期范围限定为HttpContext,而IUnitOfWork的映射将重用相同的实例作为映射的目标是一样的。