zoukankan      html  css  js  c++  java
  • 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的测试数据方法,看看追踪的效果

     

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

  • 相关阅读:
    DevOps中30 个 Docker 相关的 面试题
    Docker面试题
    微服务-服务的注册与发现
    Zookeeper 节点特性
    ElementUI 分页
    ElementUI input只允许输入数字和两位小数
    Kubernetes等待部署完成 kubectl wait rollout
    使用docker搭建selenium分布式环境
    使用Django,Prometheus,和Kubernetes定制应用指标
    使用Python和Flask编写Prometheus监控
  • 原文地址:https://www.cnblogs.com/flykai/p/3273094.html
Copyright © 2011-2022 走看看