问题 你应该如何在我的asp.net-mvc网站上使用UnitofWork模式(使用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();
       }

问题是我现在需要在我的控制器中执行Update()和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感觉不对,因为许多其他操作不需要它。我只是需要它来执行更新和插入我的数据库的操作。由于我在上面的链接上使用ninject IOC,似乎可以说通过IOC传递这个unitofwork对象。

那么,在asp.net-mvc中使用带有IOC的UnitOfWork模式是否有一种更好的更优化方式,它可以为我的控制器中的每个方法调用istransaction。


6354
2017-08-06 14:20


起源

看一眼 这个 博客文章。 - xelibrion


答案:


处理事务的另一种方法是使用 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");
}

12
2017-08-10 01:13



这正是我想要的。 Ayende提出了几乎相同的解决方案: ayende.com/blog/4809/... 但我更喜欢通过控制器请求注入会话。 - Tim


我通常会尝试将我的通用IRepository实现隐藏在IUnitOfWork中(见下文)。

我的另一个建议是将UnitOfWorkProvider或UnitOfWorkFactory传递给构造函数。这样您就可以在本地注册事务范围。这具有额外的好处,能够通过依赖注入或手动解析您认为合适的IRepository或ISession。

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

还要确保在IUnitOfWork.Dispose()中清理事务以及您可能拥有的任何数据会话对象/信息。


0
2017-08-07 07:08





我更喜欢只将我的工作单元注入实际使用它们的类中。在大多数情况下,持久性类(在我的情况下是Repository)是唯一需要工作单元的类。您希望确保保持清晰的关注点分离。控制器不需要知道工作单元,也不应该与它耦合。

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

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

如果要从控制器调用存储库,请将存储库注入控制器,如下所示:

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

那有意义吗?


0
2017-08-07 07:11



我看到的问题是你的Repository现在负责UoW。如果您需要进行需要两个存储库的更改,那么您将遇到问题。 - Phill
@Phil - 你能详细说明吗? - csano
如果您的UoW涉及两个对象,例如,UserLogin和UserProfile分别存储,没有级联,则跨两个存储库,并且您希望在事务中运行这两个对象。您的存储库当前提交了事务,因此您需要运行两个单独的工作单元。 - Phill
@Phil有道理。你会在哪里处理这笔交易?这是你在服务/业务层中做的事吗?服务/业务层是否有意义依赖您的UoW才能管理事务?如果您实现了每个请求的会话模式以及在请求期间打开了一个事务,该怎么办? - csano
我处理它的方式是基于Action by Action。在我的BaseController中,我重写OnActionExecuting以创建新的UnitOfWork,并覆盖OnActionExecuted,我调用Commit。我将UnitOfWork传递给使用它的方法(服务和/或存储库)。哦和OnException我回滚提交:) - Phill