我目前从globalasax获取会话如下......
public class MvcApplication : HttpApplication
{
public static readonly ISessionFactory SessionFactory = NHibernateHelper.CreateSessionFactory();
public MvcApplication()
{
BeginRequest += delegate
{
if (!HttpContext.Current.Request.Url.AbsolutePath.StartsWith("/_cassette/"))
{
CurrentSession = SessionFactory.OpenSession();
CurrentSession.FlushMode = FlushMode.Auto;
}
};
EndRequest += delegate
{
if (CurrentSession != null)
{
CurrentSession.Flush();
CurrentSession.Dispose();
}
};
}
public static ISession CurrentSession
{
get { return (ISession) HttpContext.Current.Items["current.session"]; }
set { HttpContext.Current.Items["current.session"] = value; }
我正在查看Sharp Architecture Transaction属性和类似的http://weblogs.asp.net/srkirkland/archive/2009/09/03/asp-net-mvc-transaction-attribute-using-nhibernate.aspx,但是在MVC4项目中处理会话的最佳方式是使用非隐式事务ala http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions
我可以通过将事务/提交添加到begin request / end请求来轻松地包装所有内容,但是属性方法看起来更干净(实际上处理错误);或者我现在应该使用过滤器吗?
使用NHibernate的MVC4的最佳实践是什么?
答案 0 :(得分:3)
您当前的会话处理存在一个严重问题(已经完成了;))。 CurrentSession是静态的,因此它在所有并发请求之间共享。 NHibernate的ISession不是线程安全的(不像ISessionFactory,它是线程安全的)。
NHibernate提供会话可以绑定到的会话上下文,之后可以从会话工厂(.GetCurrentSession() - method)获取绑定会话。为了能够像下一个示例中那样使用CurrentSessionContext,您需要告诉NHibernate使用哪个会话上下文。对于Web应用程序,WebSessionContext是不错的选择。
当我使用MVC时,我编写了一个处理会话处理的动作过滤器。这是一个例子(为MVC 2编写):
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class TransactionAttribute : ActionFilterAttribute
{
public TransactionAttribute()
{
Order = 100;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
CurrentSessionContext.Bind(NHibernateManager.SessionFactory.OpenSession());
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var session = CurrentSessionContext.Unbind(NHibernateManager.SessionFactory);
session.Close();
session.Dispose();
}
}
将事务管理添加到同一过滤器中也不应该是一个太大的问题。在OnActionExecuting-method中,您可以使用ISession的.BeginTransaction()打开事务,在OnActionExecuted中,您可以从ISession的Transaction-property获取当前事务,然后可以提交和处理该事务。
答案 1 :(得分:2)
另一种实现"每个请求模式的会话模式" - httpModule。
public class NHibernateModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += context_BeginRequest;
context.EndRequest += context_EndRequest;
}
private static void context_BeginRequest(object sender, EventArgs e)
{
//use my session manager
ISession session = SessionManager.Instance.OpenSession();
CurrentSessionContext.Bind(session);
}
private static void context_EndRequest(object sender, EventArgs e)
{
ISessionFactory sessionFactory = SessionManager.Instance.SessionFactory;
ISession session = CurrentSessionContext.Unbind(sessionFactory);
if (session == null) return;
if (session.Transaction != null)
{
if (session.Transaction.IsActive)
{
//if there is an active session, commit it
session.Transaction.Commit();
}
else
{
//
session.Transaction.Rollback();
}
}
session.Close();
}
<configuration>
<!-- IIS 6 -->
<system.web>
<httpModules>
<add name="NHibernateModule" type="NHibernateModule"/>
</httpModules>
</system.web>
<!-- IIS 7 and Cassini. -->
<system.webServer>
<modules>
<add name="NHibernateModule" type="NHibernateModule"/>
</modules>
</system.webServer>
</configuration>
ActionFilterAttribute方式有一个问题:在一个HTTP请求中如何处理一些操作?
此模式表明每个HTTP请求都会打开一个NHibernate会话。
答案 2 :(得分:0)
跟随Ultor的回答,加上Ayende的“重构无摩擦和无气味的代码:交易怎么样?”文章,最后,因为我已经使用以下内容在Application_Start中设置了应用程序的依赖项解析器:
DependencyResolver.SetResolver(new MyDependencyResolver())
我将TransactionAttribute类更改为如下:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class SessionAttribute : ActionFilterAttribute {
static readonly ISessionFactory SessionFactory = BuildSessionFactory();
static ISessionFactory BuildSessionFactory() {
return (ISessionFactory) DependencyResolver.Current.GetService(typeof (ISessionFactory));
}
public override void OnActionExecuting(ActionExecutingContext filterContext) {
var sessionController = filterContext.Controller as SessionController;
if (sessionController == null)
return;
if (sessionController.NHibernateSession == null) {
sessionController.NHibernateSession = SessionFactory.OpenSession();
}
sessionController.NHibernateSession.BeginTransaction();
CurrentSessionContext.Bind(sessionController.NHibernateSession);
}
public override void OnActionExecuted(ActionExecutedContext filterContext) {
var sessionController = filterContext.Controller as SessionController;
if (sessionController == null) return;
var session = CurrentSessionContext.Unbind(SessionFactory);
if (session == null) return;
if (session.Transaction != null) {
if (!session.Transaction.IsActive) return;
if (filterContext.Exception != null)
session.Transaction.Rollback();
else
session.Transaction.Commit();
}
session.Close();
session.Dispose();
}
}
基本控制器定义如下:
public class SessionController : Controller {
public ISession NHibernateSession { get; set; }
}
现在,我的控制器中的持久性变得如此简单:
[HttpGet, Session]
public ActionResult CreateOrUpdate(Guid id = new Guid()) {
var company = GetCompany(id);
if (company == null) throw new HttpException(404, "Not Found");
return View(company);
}
[HttpPost, ValidateAntiForgeryToken, Session]
public ActionResult CreateOrUpdate(Company passedInCompany) {
var company = NHibernateSession.Get<Company>(passedInCompany.Id);
if (company == null) throw new HttpException(404, "Not Found");
UpdateModel(company);
if (ModelState.IsValid) {
NHibernateSession.SaveOrUpdate(company);
return RedirectToAction("Index");
}
return View(company);
}
Company GetCompany(Guid id) {
Company company;
if (id == Guid.Empty) {
company = companyBuilder.Create();
} else {
company = NHibernateSession.Get<Company>(id);
NHibernateSession.Flush();
}
return company;
}
答案 3 :(得分:0)
这里有一些很好的答案,但我的建议是使用依赖注入框架(我喜欢Ninject)来实现每个请求的会话。这允许您在控制器上使用构造函数注入来注入ISession。