迷恋弦哥

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

Creating a session ASP.NET MVC action filter

使用ASP.NET MVC动作过滤器(action filter)创建会话

  通常,一个unit of work可以简洁地映射到单个的controller action. 下面介绍在ASP.NET MVC应用程序中如何通过创建一个action filter来管理我们的NHibernate会话.

准备

为NHibernate创建一个ASP.NET MVC应用程序,步骤如下:
1.   创建一个ASP.NET MVC应用程序.
2.   添加引用:NHibernate.dll,  NHibernate.ByteCode.Castle.dll, log4net.dll和Eg.Core项目.
3.   在web.config文件中,添加NHibernate和log4net的设置节点.可以参考第二章中的Configuring NHibernate with App.config小节的示例
4.   将current_session_context_class属性设置为web .
5.   在Global.asax中, 创建一个名为SessionFactory的静态属性.

View Code
public static ISessionFactory SessionFactory { get;
private set; }

6.   I在Application_Start方法中,添加下述代码:

View Code
log4net.Config.XmlConfigurator.Configure();
var nhConfig = new Configuration().Configure();
SessionFactory = nhConfig.BuildSessionFactory();

步骤

1.   添加NHibernateSessionAttribute类,代码如下:

View Code
[AttributeUsage(AttributeTargets.Method,
AllowMultiple=false)]
public class NHibernateSessionAttribute
  : ActionFilterAttribute
{
  public NHibernateSessionAttribute()
  {
    Order = 100;
  }
  protected ISessionFactory sessionFactory
  {
    get
    {
      return MvcApplication.SessionFactory;
    }
  }
  public override void OnActionExecuting(
    ActionExecutingContext filterContext)
  {
    var session = sessionFactory.OpenSession();
    CurrentSessionContext.Bind(session);
  }
  public override void OnActionExecuted(
    ActionExecutedContext filterContext)
  {
   var session = CurrentSessionContext.Unbind(sessionFactory);
   session.Close();
  }
}

2.   为controller actions布置特性,代码如下:

View Code
[NHibernateSession]
public ActionResult Index()
{
  return View(DataAccessLayer.GetBooks());
}

3.   创建一个虚拟数据访问层,代码如下:

View Code
using System.Collections.Generic;
namespace ActionFilterExample
{
  public static class DataAccessLayer
  {
    public static IEnumerable<Eg.Core.Book> GetBooks()
    {
      var session = MvcApplication.SessionFactory
        .GetCurrentSession();
      using (var tx = session.BeginTransaction())
      {
        var books = session.QueryOver<Eg.Core.Book>()
          .List();
        tx.Commit();
        return books;
      }
    }
  }
}

4.   在Views文件夹下创建一个名为Book的文件夹.

5.   在Book文件夹下,添加一个view,设置如下图所示:

6.   打开SQL Server Management Studio, 连接NHCookbook数据库,运行下面的SQL代码来创建一些book数据:

View Code
USE NHCookbook
INSERT INTO Product
VALUES (
  NEWID(),
  'Eg.Core.Book',
  0,
  'NHibernate 3 Cookbook',
  'Bridging the gap between database and .NET Application',
  45.99,
  null,
  'Jason Dentler',
  '3043'
)
INSERT INTO Product
VALUES (
  NEWID(),
  'Eg.Core.Book',
  0,

  'NHibernate 2 Beginner's Guide',
  'Rapidly retrieve data from your database into .NET objects',
  45.99,
  null,
  'Aaron Cure',
  '978-1-847198-90-7'
)

7.   编译运行.页面如下:

原理

  这个示例的理念和本章开头部分的session-per-request小节的示例是很相像的.我们使用了应用session-per-request模式的NHibernate上下文会话.
  在controller的action:Index()执行之前, ASP.NET MVC会先运行筛选器的OnActionExecuting方法.在OnActionExecuting中,通过NHibernate的上下文会话特性筛选器会打开一个会话并将她绑定到这个web请求.

  类似地,在Index()返回前,ASP.NET MVC会运行筛选器的OnActionExecuted方法.在该方法中筛选器会解除会话的绑定并将会话关闭.然后交由ASP.NET MVC处理返回的结果.在本示例中, 它呈现一个视图来显示一个书籍列表.
  action filter的Order属性决定了action filter的执行顺序. 对于执行中的事件,所有没有显式指定执行顺序的action filter会先被执行,然后那些有指定顺序的action filter才会被执行,执行顺序是:从0开始,按升序排列.对于执行完毕的事件,action filter按相反的顺序执行.本质上,就是将action filter压栈,后进先出.这样就有了一个确定的顺序, 因此我们可以将它和有更高Order值的session-dependent filters结合起来使用.

扩展

  NHibernate要求每一个数据库的交互操作都要应用NHibernate事务,无论是她是会话中方法的直接调用,还是触发延迟加载的action.通过使用这种实现,想要在事务处理中捕获延迟加载的调用时很难的.但在下个小节中我们会看到曙光,在一个单独的action filter中适当地结合使用会话和事务处理,会在其他地方实现controller action中的延迟加载.
  请确保action加载了view所需的所有数据.action的结果(这个示例中是一个view)返回后该会话不再是打开的.

提示

  因为会话已经关闭,因此如果一个view试图访问一个延迟加载的集合,而该集合尚未被controller action加载,就会抛出LazyInitializationException.

  即使有更宽松的实现,也不应从view直接访问数据库,因为view通常是动态的并且难以测试.

View 模式

  为了规避这样那样的问题,许多ASP.NET MVC应用程序使用view模式.为每个view定义一个类,该类只包含该view所需的数据. 她被认为是controller和view之间的数据传输对象.与其写几页将数据从实体复制到view模式的管道代码,不如使用一个开源项目:AutoMapper. 结合使用action filter属性,事情会变得非常简单.在Jimmy Bogard的博客上有个很不错的示例,网址是:http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/29/how-we-do-mvc-view-models.aspx.
  请注意AutoMapper属性上的Order属性.为了实现从实体到view模式转换的延迟加载,应该将Order设置为比会话属性更高的值.这将确保AutoMapper时会话是打开的.

posted on 2012-07-13 23:39  迷恋弦哥  阅读(326)  评论(0编辑  收藏  举报