Как использовать шаблон UnitofWork на моем сайте asp.net-mvc (используя nhibernate и ninject)

У меня последовал за шаблоном на этом сайте, чтобы подключить ninject и nhibernate к моему сайту asp.net-mvc3.

Вот код в моем файле global.aspx.cs:

 internal class ServiceModule : NinjectModule
{
    public override void Load()
    {
        var helper = new NHibernateHelper(connectionString);
        Bind<ISessionFactory>().ToConstant(helper.SessionFactory)
            .InSingletonScope();

        Bind<IUnitOfWork>().To<UnitOfWork>()
            .InRequestScope();
        Bind<ISession>().ToProvider(new SessionProvider())
            .InRequestScope();
        Bind<IIntKeyedRepository<FAQ>>().To<Repository<FAQ>>()
            .InRequestScope();
       }

проблема в том, что теперь мне нужно обновлять() и Add() в моих контроллерах;

У меня это как мой код контроллера:

    public FAQController(IIntKeyedRepository<FAQ> faqRepository, IUnitOfWork unitOfWork)
    {
        _faqRepository = faqRepository;
        _unitOfWork = unitOfWork;
    }


  [Authorize]
    [AcceptVerbs(HttpVerbs.Post)]
    [ValidateInput(false)]
    public ActionResult AddFAQ(FAQ contact)
    {
        var c = new FAQ {Question = contact.Question, Answer = contact.Answer};
        _faqRepository.Add(c);
        _unitOfWork.Commit();
        return RedirectToAction("Index");
    }

Мой главный вопрос заключается в том, что он ошибочно передается в Iunitofwork в конструкторе, поскольку многие другие действия ему не нужны. Мне действительно нужно это для действий, где я делаю обновления и вставляет в свой db. Поскольку я использую ninject IOC по ссылке выше, кажется, что передал этот объект unitwork через IOC.

Итак, есть ли более оптимизированный способ использования шаблона UnitOfWork с IOC в asp.net-mvc, который вызывает вызов для каждого метода в моем контроллере.

Ответ 1

Альтернативный способ обработки транзакций - использовать IActionFilter Открыть транзакцию в OnActionExecuting и зафиксировать на OnActionExecuted

public class TransactionFilter : IActionFilter
{
    private readonly ISession session;
    private ITransaction transaction;

    public TransactionFilter(ISession session)
    {
        this.session = session;
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        this.transaction = this.session.BeginTransaction();
    }

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        try
        {
            if (this.transaction.IsActive)
            {
                if (filterContext.Exception == null)
                {
                    this.transaction.Commit();
                }
                else
                {
                    this.transaction.Rollback();
                }
            }
        }
        finally
        {
            this.transaction.Dispose();
        }
    }
}

Определите атрибут для отметки действий, которые используют транзакцию:

[AttributeUsage(AttributeTargets.Method)]
public class TransactionAttribute : Attribute
{ 
}

Измените конфигурацию Ninject:

internal class ServiceModule : NinjectModule
{
    public override void Load()
    {
        var helper = new NHibernateHelper(connectionString);
        Bind<ISessionFactory>().ToConstant(helper.SessionFactory)
            .InSingletonScope();

        Bind<ISession>().ToProvider<SessionProvider>().InRequestScope();
        Bind(typeof(IRepository<>)).To(typeof(Repository<>));
        Bind(typeof(IIntKeyedRepository<>)).To(typeof(Repository<>));
        BindFilter<TransactionFilter>(FilterScope.Action, null)
            .WhenActionMethodHas<TransactionAttribute>();
    }
}

Наконец, измените свой контроллер:

public FAQController(IIntKeyedRepository<FAQ> faqRepository)
{
    _faqRepository = faqRepository;
}


[Transaction]
[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
[ValidateInput(false)]
public ActionResult AddFAQ(FAQ contact)
{
    var c = new FAQ {Question = contact.Question, Answer = contact.Answer};
    _faqRepository.Add(c);
    return RedirectToAction("Index");
}

Ответ 2

Обычно я стараюсь, чтобы моя общая реализация IRepository скрывалась внутри IUnitOfWork (см. ниже).

Моя другая рекомендация - передать конструктору UnitOfWorkProvider или UnitOfWorkFactory. Таким образом, вы можете зарегистрировать область транзакций локально. Это дает дополнительное преимущество в том, что вы можете разрешить IRepository или ISession по своему усмотрению, путем инъекции зависимостей или вручную.

using(var uow = this.UnitOfWorkProvider.New())
{
    uow.Save<Faq>(myFaq);
}

Также убедитесь, что вы в своем IUnitOfWork.Dispose() очищаете транзакцию и любые объекты/информацию сеанса данных, которые у вас могут быть.

Ответ 3

Я предпочитаю только вводить свою единицу работы в классы, которые на самом деле их используют. В большинстве случаев классы персистентности (репозиторий в моем случае) являются единственными, которые нуждаются в единице работы. Вы хотите, чтобы вы поддерживали чистое разделение проблем. Контроллер не должен знать об устройстве работы и не должен быть связан с ним.

public class FaqRepository {
  public FaqRepository(IUnitOfWork unitofWork) { ... }

  public void CreateQuestion(Faq faq) {
    unitOfWork.Save(faq);
    unitOfWork.Commit();
  }
}

Если вы вызываете свой репозиторий с вашего контроллера, введите репозиторий в свой контроллер следующим образом:

public class FaqController {
  public FaqController(IFaqRepository faqRepository) {...}
}

Это имеет смысл?