UoW的nHibernate [TransactionAttribute]与Repository Pattern冲突

时间:2011-03-17 18:50:52

标签: asp.net-mvc-3 fluent-nhibernate transactions

研究设计IRepository<T>结构的最佳方法,我在查看NHProf的一些论坛时遇到了一个名为'Whiteboard'的项目(http://whiteboardchat.codeplex.com/) 。

我挖了一段时间的源代码,发现一个名为TransactionAttribute的MVC非常有趣的属性,定义如下; (我做了一些简短的调整以适应我的IoC解决方案)

using System;
using System.Linq;

using Ninject;

namespace System.Web.Mvc
{
    /// <summary>
    /// This will allow ASP.NET MVC to apply Transactions to the controllers.
    /// </summary>
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
    public class TransactionAttribute : ActionFilterAttribute
    {
        [Inject]
        public NHibernate.ISession Session 
        { 
            get; 
            set; 
        } 

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            Session.BeginTransaction();
        }

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (Session.Transaction.IsActive)
            {
                if (filterContext.Exception == null)
                {
                    Session.Flush();
                    Session.Transaction.Commit();
                }
                else
                {
                    Session.Transaction.Rollback();
                }
            }
        }
    }
}

这真的很有趣;而且很有用,不管它有什么困扰我。当我使用NHProf运行查询时,它会向我发出有关“未正确使用事务”的警告,并建议我将所有查询包装在Transaction中。好吧,这很好,很好......

然后我去装饰我的Repository<T> : IRepository<T>类这样......

    public T Update(T instance)
    {
        using (var transaction = session.BeginTransaction())
        {
            // attempt to perform the given update
            session.SaveOrUpdate(instance);

            try
            {
                // commit the transaction to the database
                transaction.Commit();

                // update succeeded, so we'll return true
                return instance;
            }
            catch
            {
                // restore the database to its previous state if we failed.
                transaction.Rollback();

                // update failed, so return a null object
                return default(T);
            }
        }
    }

这是我遇到的问题。

我读到的每个地方,常规练习总是使用Repository添加到集合中。然而TransactionAttribute本身是由 Ayende Rahien 的博客引起我注意的,他来自我可以收集NHProf的主要开发者之一,以及在 Whiteboard 项目中工作的人之一,假设您正在MVC控制器级别执行 Repository 命令。

那是哪个?我现在完全混淆了我的交易逻辑应该用于最佳实践的地方。我实际上找到了相互矛盾的答案,在某些情况下来自同一个人。

3 个答案:

答案 0 :(得分:1)

您不应该处理存储库中的事务。控制器(就像你有)或HTTP模块应该启动和提交/回滚事务。保存或更新不应该孤立地完成。它们将在操作结束时由控制器提交。这样您就可以利用ADO批处理和其他NHibernate功能。

另外,请确保将nhibernate ISession的FlushMode设置为Commit。

答案 1 :(得分:0)

您是否使用[Transaction]属性修饰了操作方法或控制器类?如果没有,则甚至不会调用此动作过滤器代码。

此外,您还需要确保将会话对象[Inject]放入存储库,并确保会话对象的作用域为请求。

作为一个例子:

public class MyRepository
{
    [Inject]
    public ISession Session { get; set; }

    public void Save(MyModel model) { Session.Save(model); }
}

public class MyController : Controller
{
    [Inject]
    public MyRepository MyRepository { get; set; }

    [Transaction]
    public ActionResult Save(MyModel model)
    {
        MyRepository.Save(model);
    }
}

以及注册会话时;

var configuration = new NHibernateConfiguration();
Bind<ISessionFactory>().ToConstant(configuration.GetSessionFactory());
Bind<ISession>().ToMethod(x => x.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();

注意InRequestScope()部分

答案 2 :(得分:0)

为@Fatal发布此内容。

最初的回答回答了我的问题,但这不可避免地是为了避免使用方法级属性而最终做的事情。

我没有声明代码来控制属性中的事务,而是将其包含在我的ISession Ninject管理中。

        Bind<ISession>()
            .ToMethod(c => OpenSession())
            .InRequestScope()
            .OnActivation(session =>
            {
                session.BeginTransaction();
                session.FlushMode = FlushMode.Commit;
            })
            .OnDeactivation(session =>
            {
                if (session.Transaction.IsActive)
                {
                    try
                    {
                        session.Transaction.Commit();
                    }
                    catch
                    {
                        session.Transaction.Rollback();
                    }
                }
            });

这样做会打开注入了ISession的新ISession每个请求,当它被激活时,它会开始一个新的事务。此时,Ninject现在跟踪状态并处理回滚的后果,从而实现了一个非常简单的工作单元模式。

我不知道这是否是世界上最好的方法,但我已经向少数人展示了它并没有因为不良做法而被击落,而且到目前为止它对我来说效果很好。