NHibernate – One Session Per Request

Ao trabalhar com NHibernate, um dos principais desafios é gerenciar a Session. Podemos abrir a Session a cada consulta, mas isso seria bem custoso, pois em muitos casos precisamos recorrer ao banco de dados diversas vezes, além de perder uma das grandes vantagens do NHibernate que é utilizar o Lazy Load.
Uma maneira comum de se trabalhar com a session seria da maneira que eu mostro neste post, que ficaria mais ou menos assim:

        using (ISession session = NHibernateHelper.OpenSession()) { }

Porém, se trabalhamos desta maneira, e buscamos uma lista de usuários por exemplo. O código ficaria assim:

    public IList<User> GetAll()
    {
        using (ISession session = NHibernateHelper.OpenSession())
        {
            var users = session.CreateCriteria<User>().List<User>();
            return users;
        }
    }

Porém, imagine o caso que o cada usuário possui um endereço e uma lista de músicas favoritas. Devido ao Lazy Load do NHibernate, nós precisaríamos pedir para o NHibernate buscar também estas dependências:

    public IList<User> GetAll()
    {
        using (ISession session = NHibernateHelper.OpenSession())
        {
            var users = session.CreateCriteria<User>().List<User>();
            foreach (var user in users)
            {
                NHibernateUtil.Initialize(user.Adress);
                NHibernateUtil.Initialize(user.Adress.City);
                NHibernateUtil.Initialize(user.Adress.State);
                NHibernateUtil.Initialize(user.Adress.Country);
            }
            return users;
        }
    }

Agora se aproveitássemos o Lazy Load do NHibernate, nós poderíamos buscar apenas os usuários, e caso chamássemos alguma dependência, o nhibernate a inicializaria para nós, apenas quando precisamos. Uma forma de trabalhar desta maneira é utilizar uma sessão por requisição. Desta maneira quando a requisição é iniciada, nós abrimos uma sessão e a mantemos até que a requisição termine. Em asp.net mvc, eu venho trabalhando da maneira que vou demonstrar aqui. Pode não ser a mais elegante ou eficiente, mas tem sido bem prático e fácil, além e não me trazer problemas até agora. Eu inicializo a session no evento Application_BeginRequest e fecho a session no evento Application_EndRequest em Global.asax.cs, ficando assim:

    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            AuthConfig.RegisterAuth();
        }

        protected void Application_EndRequest(object sender, EventArgs e)
        {
            if (CurrentSession != null)
                CurrentSession.Dispose();
        }

        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            CurrentSession = NHibernateHelper.SessionFactory.OpenSession();
        }

        public static ISession CurrentSession
        {
            get { return (ISession)HttpContext.Current.Items["current.session"]; }
            set { HttpContext.Current.Items["current.session"] = value; }
        }
    }

E o NHibernateHelper fica assim:

    public static class NHibernateHelper
    {
        private static ISessionFactory _sessionFactory;
        private static Configuration _configuration;
        private static HbmMapping _mapping;

        public static ISession OpenSession()
        {
            return SessionFactory.OpenSession();
        }

        public static IStatelessSession OpenStatelessSession()
        {
            return SessionFactory.OpenStatelessSession();
        }

        public static ISessionFactory SessionFactory
        {
            get
            {
                if (_sessionFactory == null)
                {
                    _sessionFactory = Configuration.BuildSessionFactory();
                }
                return _sessionFactory;
            }
        }

        public static Configuration Configuration
        {
            get
            {
                if (_configuration == null)
                {
                    _configuration = CreateConfiguration();
                }
                return _configuration;
            }
        }

        public static HbmMapping Mapping
        {
            get
            {
                if (_mapping == null)
                {
                    _mapping = CreateMapping();
                }
                return _mapping;
            }
        }

        private static Configuration CreateConfiguration()
        {
            var configuration = new Configuration();
            configuration.Configure();
            configuration.AddDeserializedMapping(Mapping, null);

            return configuration;
        }

        private static HbmMapping CreateMapping()
        {
            var mapper = new ModelMapper();
            mapper.AddMappings(new List<System.Type> { typeof(UserMap) });
            
            return mapper.CompileMappingForAllExplicitlyAddedEntities();
        }
    }

Note que desta maneira, fica muito fácil injetar a session em nossos repositórios / daos, etc caso estivermos utilizando algum ioc container.

Por hoje é só, abraço!

Anúncios

2 comentários sobre “NHibernate – One Session Per Request

  1. Olá Rafael parabéns pelo post.

    Existe alguma maneira de filtrar essas requisições, para que quando for uma requisição de arquivos js ou css ou imagens, não criar instancias do objeto ISession a toa?

    1. opa, tem sim!
      é só pegar a url do request e verificar o que está vindo. Segue um exemplo em que ele verifica se o request possui um ponto e entre 1 a 3 caracteres após o ponto, por exemplo .css ou .js

          var url = Request.Url.AbsolutePath;
          if (Regex.IsMatch(url, "\\.\\w{1,3}") == false)
              CurrentSession = NHibernateHelper.SessionFactory.OpenSession();
      

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s