研究设计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 命令。
那是哪个?我现在完全混淆了我的交易逻辑应该用于最佳实践的地方。我实际上找到了相互矛盾的答案,在某些情况下来自同一个人。
答案 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现在跟踪状态并处理回滚的后果,从而实现了一个非常简单的工作单元模式。
我不知道这是否是世界上最好的方法,但我已经向少数人展示了它并没有因为不良做法而被击落,而且到目前为止它对我来说效果很好。