NHibernate Session-per-request and MiniProfiler.NHibernate

前言

  1、本文以mvc3为例,借鉴开源项目 NerdDnner项目完成nhibernate中的 Session-per-request 模式,本文创建了一个自定义的httpmodel类,来实现在http请求的时候创建并开启一个session并绑定到CurrentSessionContext中,当请求完成以后关闭,同时包含对事物的处理。

  2、利用MiniProfiler.NHibernate来追踪项目中的产生的sql,便于我们及时发现问题及时处理。MiniProfiler.NHibernate现在可以在nuget上直接获取或者可以去github中下载源码查看。

实现Session per request

public class NHibernateSessionPerRequest : IHttpModule
    {
        private static readonly ISessionFactory sessionFactory;

        //构造函数
        static NHibernateSessionPerRequest()
        {
            sessionFactory = CreateSessionFactory();
        }

        // 初始化httpmodel
        public void Init( HttpApplication context )
        {
            context.BeginRequest += BeginRequest;
            context.EndRequest += EndRequest;
        }

        public void Dispose() { }

        public static ISession GetCurrentSession()
        {
            return sessionFactory.GetCurrentSession();
        }

        // 打开session, 开启事务, 绑定session到CurrentSessionContext
        private static void BeginRequest( object sender, EventArgs e )
        {
            ISession session = sessionFactory.OpenSession();

            session.BeginTransaction();

            CurrentSessionContext.Bind( session );
        }

        // 移除session会话, 事物提交, and 关闭session会话
        private static void EndRequest( object sender, EventArgs e )
        {
            ISession session = CurrentSessionContext.Unbind( sessionFactory );

            if ( session == null ) return;

            try
            {
                session.Transaction.Commit();
            }
            catch ( Exception )
            {
                session.Transaction.Rollback();
            }
            finally
            {
                session.Close();
                session.Dispose();
            }
        }

        // 创建sessionfactory
        private static ISessionFactory CreateSessionFactory()
        {
            return Fluently.Configure(new Configuration().Configure())
                .Mappings( m => m.AutoMappings.Add( CreateMappings() ) )
                .ExposeConfiguration( UpdateSchema )
                .CurrentSessionContext<WebSessionContext>()
                .BuildSessionFactory();
        }
        
        private static AutoPersistenceModel CreateMappings()
        {
            return AutoMap
                .Assembly( System.Reflection.Assembly.GetCallingAssembly() )
                .Where( t => t.Namespace != null && t.Namespace.EndsWith( "Models" ) )
                .Conventions.Setup( c => c.Add( DefaultCascade.SaveUpdate() ) );
        }
        
        // 生成数据库架构
        private static void UpdateSchema( Configuration cfg )
        {
            new SchemaUpdate( cfg )
                .Execute( false, true );
        }
    }

此类的nh的配置采用fluent的方式配置映射,可以生成数据库架构,ISessionFactory在每次请求中只会生成一次,当我们需要session的时候只需调用GetCurrentSession方法,当http请求的时候session创建并存储在CurrentSessionContext.Bind()中,并开启事物操作,当请求结束的时候 CurrentSessionContext.Unbind()移除session,事物提交并将session关闭。这里存在一些问题:尽管session是非常轻量级的这样每一次http请求都会去创建session,并不能做到我们真正需要的时候去创建。

注册httpmodel:

在web.config中添加如下2处节点:

测试程序

Models:

public class Employee
    {
        public virtual int Id { get; protected set; }
        public virtual string FirstName { get; set; }
        public virtual string LastName { get; set; }
        public virtual Store Store { get; set; }
    }
public class Product
    {
        public virtual int Id { get; protected set; }
        public virtual string Name { get; set; }
        public virtual double Price { get; set; }
        public virtual IList<Store> StoresStockedIn { get; protected set; }

        public Product()
        {
            StoresStockedIn = new List<Store>();
        }
    }
public class Store
    {
        public virtual int Id { get; protected set; }
        public virtual string Name { get; set; }
        public virtual IList<Product> Products { get; protected set; }
        public virtual IList<Employee> Staff { get; protected set; }

        public Store()
        {
            Products = new List<Product>();
            Staff = new List<Employee>();
        }

        public virtual void AddProduct( Product product )
        {
            product.StoresStockedIn.Add( this );
            Products.Add( product );
        }

        public virtual void AddEmployee( Employee employee )
        {
            employee.Store = this;
            Staff.Add( employee );
        }
    }
View Code

Repositories:

public interface IRepository<T>
    {
        IQueryable<T> GetAll();
        IQueryable<T> Get( Expression<Func<T, bool>> predicate );
        IEnumerable<T> SaveOrUpdateAll( params T[] entities );
        T SaveOrUpdate( T entity );
    }
public class Repository<T> : IRepository<T>
    {
        private readonly ISession session;

        public Repository()
        {
            session = NHibernateSessionPerRequest.GetCurrentSession();
        }

        public IQueryable<T> GetAll()
        {
            return session.Query<T>();
        }

        public IQueryable<T> Get( Expression<Func<T, bool>> predicate )
        {
            return GetAll().Where( predicate );
        }

        public IEnumerable<T> SaveOrUpdateAll( params T[] entities )
        {
            foreach ( var entity in entities )
            {
                session.SaveOrUpdate( entity );
            }

            return entities;
        }

        public T SaveOrUpdate( T entity )
        {
            session.SaveOrUpdate( entity );

            return entity;
        }
    }
View Code

HomeController:

public class HomeController : Controller
    {
        private readonly IRepository<Store> storeRepository;
        public HomeController()
        {
            storeRepository = new Repository<Store>();
        }
        public ActionResult Index()
        {
            var stores = storeRepository.GetAll();

            return View(stores.ToList());
        }
        public ActionResult Test()
        {
            var barginBasin = storeRepository.Get(s => s.Name == "Bargin Basin").SingleOrDefault();

            if (barginBasin == null)
            {
                return RedirectToAction("Index");
            }

            barginBasin.Name = "Bargain Basin";

            return RedirectToAction("Index");
        }

        public ActionResult Seed()
        {
            var barginBasin = new Store { Name = "Bargin Basin" };
            var superMart = new Store { Name = "SuperMart" };

            var potatoes = new Product { Name = "Potatoes", Price = 3.60 };
            var fish = new Product { Name = "Fish", Price = 4.49 };
            var milk = new Product { Name = "Milk", Price = 0.79 };
            var bread = new Product { Name = "Bread", Price = 1.29 };
            var cheese = new Product { Name = "Cheese", Price = 2.10 };
            var waffles = new Product { Name = "Waffles", Price = 2.41 };

            var daisy = new Employee { FirstName = "Daisy", LastName = "Harrison" };
            var jack = new Employee { FirstName = "Jack", LastName = "Torrance" };
            var sue = new Employee { FirstName = "Sue", LastName = "Walkters" };
            var bill = new Employee { FirstName = "Bill", LastName = "Taft" };
            var joan = new Employee { FirstName = "Joan", LastName = "Pope" };


            AddProductsToStore(barginBasin, potatoes, fish, milk, bread, cheese);
            AddProductsToStore(superMart, bread, cheese, waffles);


            AddEmployeesToStore(barginBasin, daisy, jack, sue);
            AddEmployeesToStore(superMart, bill, joan);

            storeRepository.SaveOrUpdateAll(barginBasin, superMart);

            return RedirectToAction("Index");
        }

        private void AddProductsToStore(Store store, params Product[] products)
        {
            foreach (var product in products)
            {
                store.AddProduct(product);
            }
        }

        private void AddEmployeesToStore(Store store, params Employee[] employees)
        {
            foreach (var employee in employees)
            {
                store.AddEmployee(employee);
            }
        }
    }
View Code

完成以后,修改hibernate.cfg.xml中的链接字符串等,并将其属性复制到输出目录修改为:如果较新则复制,运行程序请求index方法,将会产生数据库架构。

配置MiniProfiler.NHibernate

1、使用nuget控制台Install-Package MiniProfiler.NHibernate安装,或者参考github中的代码自己写一个

2、修改NH配置文件中的数据库驱动类,将connection.driver_class几点替换为

<property name="connection.driver_class">
      StackExchange.Profiling.NHibernate.Drivers.MiniProfilerSql2008ClientDriver,StackExchange.Profiling.NHibernate
    </property>

3、在模板页中中的<head>节点添加@MiniProfiler.RenderIncludes()方法调用,修改Global.asax,添加如下代码:

protected void Application_BeginRequest()
        {
            if (Request.IsLocal)
            {
                MiniProfiler.Start();
            }
        }

        /// <summary>
        /// 终止时结束
        /// </summary>
        protected void Application_EndRequest()
        {
            MiniProfiler.Stop();
        }

配置完成后运行程序,便可以看到MiniProfiler.NHibernate的效果了,使用它可以帮我们监控nh产生的sql及时优化代码,举个例子,运行请求home/seed的测试数据方法,看看追踪的效果

 

文章结束,时间仓促代码粗略,文中若有不合理的地方,欢迎批评指正。

 

转载于:https://www.cnblogs.com/flykai/p/3273094.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值