简体   繁体   English

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

[英]Dependency injection with EF DbContext that implements 2 interfaces

Given a DbContext that implements 2 interfaces like so: 给定一个实现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
}

First question, is it a bad idea to separate out the query aspects of DbContext (IDbSets) from the command aspects (SaveChanges)? 第一个问题,将DbContext(IDbSets)的查询方面与命令方面(SaveChanges)分开是一个坏主意吗? I am exploring a refactor to the above because there are a lot of cases where we just need to query data, without saving anything. 我正在探索对上述内容的重构,因为在很多情况下,我们只需要查询数据而不保存任何内容。

The problem I'm running into involves unity DI, which currently injects MyDbContext using a singleton-per-http-context lifetime for the IUnitOfWork interface. 我遇到的问题涉及统一DI,该DI目前使用IUnitOfWork接口的每HTTP上下文生存期注入MyDbContext。 I'm not sure how to go about setting up injection for the IQueryEntities interface so that it will reuse an existing DbContext instance that may have already been injected against the IUnitOfWork interface. 我不确定如何为IQueryEntities接口设置注入,以便它将重用可能已经针对IUnitOfWork接口注入的现有DbContext实例。 Or vice versa. 或相反亦然。 Is this even possible? 这有可能吗?

Here is the current lifetime manager that reuses previously-injected instances of IUnitOfWork in the same http context: 这是当前的生存期管理器,它在相同的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);
    }
}

By the way if there is a way to do this, I would prefer to do it in unity web.config section rather than compiled c# bootstrapper. 顺便说一句,如果有办法做到这一点,我宁愿在统一的web.config部分而不是编译的c#bootstrapper中进行。

Update 更新

With help from onof I was able to get this working, however my config looks different from what he suggested. 在onof的帮助下,我可以使它正常工作,但是我的配置看起来与他的建议不同。 Am I doing something wrong? 难道我做错了什么? When I don't give each interface the lifetime manager, one HttpContext ends up with multiple instances of the DbContext. 当我不为每个接口提供生命周期管理器时,一个HttpContext最终会带有DbContext的多个实例。 Only when I give all 3 the lifetime manager does it reuse the same DbContext instance across a single request for both interfaces. 只有当我给所有3个生命周期管理器时,它才会在针对两个接口的单个​​请求中重用同一DbContext实例。 Is something wrong with this config? 此配置有问题吗?

<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>

is it a bad idea to separate out the query aspects of DbContext (IDbSets) from the command aspects (SaveChanges)? 将DbContext(IDbSets)的查询方面与命令方面(SaveChanges)分开是一个坏主意吗?

I think it's a good idea, because of Interface Segregation Principle , which states that each client should see only the interface it needs to do its work. 我认为这是个好主意,因为有接口隔离原则 ,该原则规定每个客户端仅应看到其工作所需的接口。

To register, i would do: 要注册,我会做:

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

AFAIK it's the only way to share the same instance, once the object created. 创建对象后,AFAIK是共享同一实例的唯一方法。

To do it at design-time (in web.config), it's straightforward: 要在设计时(在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>

You need to register one interface as singleton and the other will automatically follow. 您需要将一个接口注册为单例,而另一个将自动跟随。

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

Assuming that your LifetimeManager works correctly this would scope the lifetime of a instance of MyContext to the HttpContext and the mapping from IUnitOfWork would reuse the same instance as the target of the mapping is the same. 假设您的LifetimeManager正常工作,这会将MyContext实例的生存期限MyContextHttpContext并且IUnitOfWork的映射将重用同一实例,因为映射的目标是相同的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM