Как использовать шаблон 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) {...}
}
Это имеет смысл?