UoW 的 nHibernate [TransactionAttribute] 与存储库模式冲突
在研究设计 IRepository
结构的最佳方法时,我在浏览一些论坛时发现了一个名为“Whiteboard”的项目 (http://whiteboardchat.codeplex.com/) >NHProf。
我研究了它的源代码一段时间,发现了一个非常有趣的 MVC 属性,称为 TransactionAttribute
,定义如下; (我做了简短的调整以适应我的 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
像这样的类...
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);
}
}
}
这是我遇到的问题。
在我阅读的所有地方,常见做法是始终使用存储库来添加到集合中。然而,TransactionAttribute
本身是由 Ayende Rahien 的博客引起我注意的,据我所知,他是 NHProf 的主要开发人员之一
以及从事此 Whiteboard 项目的人员之一,假设您正在 MVC 控制器级别执行存储库 命令。
那么是哪一个呢?我现在完全困惑我的事务逻辑应该在哪里实现最佳实践。我确实找到了相互矛盾的答案,并且在某些情况下来自同一个人。
Doing research into the best way to design IRepository<T>
structures, I came across a project called 'Whiteboard' (http://whiteboardchat.codeplex.com/) while looking through some forums for NHProf
.
I dug around its source code for a while, and found a really interesting attribute for MVC called TransactionAttribute
, defined as follows; (I have made brief adjustment to suit my IoC solution)
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();
}
}
}
}
}
This is really interesting; And useful, however something about it bothers me. When I run my queries using NHProf
, it gives me warnings about 'Not using transactions properly', and suggests I wrap all queries in a Transaction
. Alright, that's fine and good...
So then I go and decorate my Repository<T> : IRepository<T>
class like this ...
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);
}
}
}
Here's the problem I am running into.
Everywhere I read, the common practice is to always use a Repository for adding to the collections. However the TransactionAttribute
, which in itself was brought to my attention by Ayende Rahien's blog, who is from what I can gather one of the primary developers of NHProf
, and one of the people working on this Whiteboard project, makes the assumption that you are performing Repository commands at the MVC Controller Level.
So which is it? I'm utterly confused now where my Transaction logic is supposed to go for the best practice. I'm literally finding conflicting answers, and in some cases from the same people.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您不应该处理存储库内的事务。控制器(就像您拥有的)或 HTTP 模块应该启动并提交/回滚事务。保存或更新不应单独完成。它们将由控制器在操作结束时提交。这样您就可以利用 ADO 批处理和其他 NHibernate 功能。
另外,请确保将 nhibernate ISession 的 FlushMode 设置为 Commit。
You are not supposed to deal with transactions inside repositories. A controller (like you have) or HTTP module should start and commit/rollback transactions. Saves or updates are not supposed to be done in isolation. They will be committed at the end of the operation by the controller. This way you can take advantage of ADO batching and other NHibernate features.
Also, make sure to set the FlushMode of the nhibernate ISession to Commit.
您是否使用
[Transaction]
属性来装饰您的操作方法或控制器类?如果不是,则甚至不会调用此操作过滤器代码。此外,您还需要确保将会话对象也
[注入]
到您的存储库中,并且会话对象的范围仅限于请求。例如:
在注册您的会话时;
注意
InRequestScope()
部分Are you decorating your action method or controller class with the
[Transaction]
attribute? If not, this action filter code won't even be called.Also, you will need to ensure that you
[Inject]
the session object into your repository as well and that the session object is scoped to the request.as an example:
and when registering your session;
notice the
InRequestScope()
part为@Fatal 发布此内容。
最初的回答回答了我的问题,但这不可避免地是我最终所做的以避免使用方法级属性。
我没有在属性中声明控制事务的代码,而是将其直接包含在 Ninject 的
ISession
管理中。其作用是在注入
ISession
的每个请求中打开新的ISession
,并且当它被激活时,它会开始一个新事务。此时,Ninject 现在跟踪状态并处理回滚状态的后果,从而实现非常简单的工作单元模式。我不知道这是否是世界上最好的方法,但我已经向一些人展示了它,并且它没有因为不好的做法而被否决,而且到目前为止对我来说效果很好。
Posting this for @Fatal.
The original response answered my question, but this is inevitably what I ended up doing to avoid using method level attributes.
Instead of declaring the code to control my transaction in an attribute, I included it right in my
ISession
management for Ninject.What this does is open the new
ISession
each request where anISession
is injected, and when it is activated it begins a new transaction. At this point, Ninject now tracks the state and handles the consequences of rolling it back, thus implementing a very simplistic unit of work pattern.I do not know if this is the best approach in the world, but I have shown it to a few people and it has not been shot down for bad practice, and it has worked well for me so far.