Как правильно распоряжаться объектами, зарегистрированными в Autofac

Я реализовал шаблон Unit of Work/Repository, как описано здесь, но я также использую autofac и внедрение конструктора, поэтому я зарегистрировал UnitOfWork и Класс DbContext (PsyProfContext) выглядит следующим образом:

 builder.Register(context => new PsyProfContext()).InstancePerHttpRequest();
 builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerHttpRequest();

И все отлично работает!

За исключением одного: я также использую enterprise блок ведения журнала библиотеки, и я реализовал CustomTraceListener, который использует Entity Framework для записи записи журнала в базу данных.

Мой контроллер выглядит так (он пуст, потому что в данный момент я просто пытался проверить, все ли работает (IoC, ведение журнала, структура сущностей)):

public class HomeController : Controller
{
    private readonly UnitOfWork unitOfWork;

    public HomeController(IUnitOfWork unitOfWork)
    {
        this.unitOfWork = (UnitOfWork) unitOfWork;
    }

    //
    // GET: /Home/
    public ActionResult Index()
    {
        throw new HttpException();
        return View();
    }

    protected override void Dispose(bool disposing)
    {
        unitOfWork.Dispose();
        base.Dispose(disposing);
    }
}

И в методе Write класса CustomTraceListener я попытался разрешить UnitOfWork:

DependencyResolver.Current.GetService<IUnitOfWork>() as UnitOfWork;

Но я получаю экземпляр, который уже утилизирован! поэтому я поставил несколько точек останова и обнаружил, что метод Dispose контроллера вызывается перед методом Write класса CustomTraceListener, поэтому в итоге я не нашел другого решения, кроме прямого использования DbContext (PsyProfContext):

public override void Write(object o)
    {
        using (var conext = new PsyProfContext())
        {
            var customLogEntry = o as CustomLogEntry;

            if (customLogEntry != null)
            {
                var logEntry = new LogEntry
                                   {
                                       //a bunch of properties
                                   };

                conext.Exceptions.Add(logEntry);
                conext.SaveChanges();
            }
        }
    }

Но мне не нравится это решение! Какой смысл использовать шаблон UnitOfWork и Repository, если вы напрямую обращаетесь к объекту DbContext. Или какой смысл использовать DI в проекте, если в некоторых случаях вы создаете зарегистрированный объект вручную.

Вот я и хотел услышать ваше мнение, как поступать в подобных ситуациях? Моя текущая реализация в порядке, или она определенно неверна, и я должен подумать о другой.

Любая помощь будет принята с благодарностью, и любые идеи приветствуются!


person Peace87    schedule 01.04.2013    source источник
comment
Это не актуально, но зачем вы кастуете свои услуги? В любом случае причина, по которой ваша служба не разрешена, вероятно, заключается в том, что HttpContext имеет значение null в то время, когда вы пытаетесь разрешить.   -  person maxlego    schedule 01.04.2013
comment
Есть ли причина, по которой вам нужно привязывать ведение журнала к единице работы? Обычно Единица работы не фиксируется, если выдается исключение или обнаруживается какая-либо ошибка проверки, но ведение журнала должно пройти успешно, несмотря ни на что. На самом деле, часто во время ошибок ведение журнала является наиболее ценным!   -  person default.kramer    schedule 02.04.2013
comment
Вы совершенно правы насчет кастинга, я не обратил на это внимание.   -  person Peace87    schedule 02.04.2013
comment
@ default.kramer Я согласен с вами в том, что ведение журнала должно быть успешным, несмотря ни на что, поэтому я настроил несколько прослушивателей, я имею в виду, что в данный момент исключение будет зарегистрировано в журнале событий Windows И в базе данных, поэтому все сведения об исключении будут сохраняться в таблицу. Также в будущем я планирую добавить несколько дополнительных слушателей, таких как электронная почта (в случае критической проблемы) и запись в файл.   -  person Peace87    schedule 02.04.2013


Ответы (1)


Похоже, у вас может быть пара проблем.

Во-первых, если вы вручную размещаете единицу работы в своем контроллере, ваш контроллер должен принимать Owned<IUnitOfWork> в конструкторе. Когда время жизни запроса будет удалено, оно автоматически удалит любые компоненты IDisposable, включая контроллер и любые разрешенные зависимости, если вы каким-то образом не укажете, что собираетесь взять на себя ответственность за время жизни. Это можно сделать с помощью Owned<T>.

public class HomeController : Controller
{
  Owned<IUnitOfWork> _uow;
  public HomeController(Owned<IUnitOfWork> uow)
  {
    this._uow = uow;
  }
  protected override void Dispose(bool disposing)
  {
    if(disposing)
    {
      this._uow.Dispose();
    }
    base.Dispose(disposing);
  }
}

(Обратите внимание на незначительное логическое исправление в переопределении Dispose — вам нужно проверить значение disposing, чтобы не утилизировать свою единицу работы дважды.)

В качестве альтернативы вы можете зарегистрировать свои единицы работы как ExternallyOwned, например

builder
  .RegisterType<UnitOfWork>()
  .As<IUnitOfWork>()
  .ExternallyOwned()
  .InstancePerHttpRequest();

ExternallyOwned также сообщает Autofac, что вы возьмете на себя управление утилизацией. В этом случае ваш контроллер будет выглядеть так, как он уже есть. (Обычно я предпочитаю просто позволить Autofac сделать всю работу и не брать на себя ответственность, если можно этого избежать.)

На самом деле, глядя на то, как все устроено, можно вообще избежать проблемы утилизации, если вы позволите Autofac сделать это за вас — вызов DependencyResolver вернет единицу работы, которая еще не утилизирован, и все было бы в порядке.

Если это не исправит ... вы можете добавить некоторые детали к своему вопросу. Я вижу, где ваш контроллер использует класс единицы работы, но я не вижу, где он что-либо регистрирует, и я не вижу ничего в реализации слушателя, использующей единицу работы.

(Кроме того, как отмечено в первом комментарии к вашему вопросу, в конструкторе вашего контроллера вы не должны переводить свою службу с IUnitOfWork на UnitOfWork - это нарушает абстракцию, которую интерфейс предлагал в первую очередь.)

person Travis Illig    schedule 01.04.2013
comment
Вы правы, позволяя Autofac автоматически избавляться от любых компонентов IDisposable. Я не знал об этой функции, поэтому я обязательно должен прочитать вики Autofac. - person Peace87; 02.04.2013