Внедрение зависимостей с EF DbContext, который реализует 2 интерфейса - PullRequest
2 голосов
/ 31 января 2012

Учитывая DbContext, который реализует 2 интерфейса следующим образом:

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 (IDbSets) от аспектов команды (SaveChanges)?Я изучаю рефакторинг вышеупомянутого, потому что во многих случаях нам просто нужно запрашивать данные, не сохраняя ничего.

Проблема, с которой я сталкиваюсь, включает в себя Uni DI, который в настоящее время внедряет MyDbContext, используя время жизни singleton-per-http-context для интерфейса IUnitOfWork.Я не уверен, что делать с настройкой внедрения для интерфейса IQueryEntities, чтобы он повторно использовал существующий экземпляр DbContext, который, возможно, уже был внедрен в интерфейс IUnitOfWork.Или наоборот.Это вообще возможно?

Вот текущий менеджер времени жизни, который повторно использует ранее внедренные экземпляры IUnitOfWork в том же http-контексте:

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);
    }
}

Кстати, если есть способ сделатьЭто, я бы предпочел сделать это в разделе Uni Web.config, а не скомпилированный загрузчик C #.

Обновление

С помощью 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 ]

3 голосов
/ 31 января 2012

Это плохая идея, чтобы отделить аспекты запроса 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>
2 голосов
/ 31 января 2012

Вам необходимо зарегистрировать один интерфейс как синглтон, а другой будет автоматически следовать.

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

Если предположить, что ваш LifetimeManager работает правильно, это увеличит время жизни экземпляра MyContext до HttpContextи сопоставление из IUnitOfWork будет повторно использовать тот же экземпляр, так как цель сопоставления совпадает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...