【问题标题】:Am I using nHibernate's ISession properly here?我在这里正确使用 nHibernate 的 ISession 吗?
【发布时间】:2012-06-13 22:15:07
【问题描述】:

我从 nHibernate 开始,并想从第一天开始就这样做,所以这就是我使用 ISession 的方式。 (每个请求的网络)

这是我的助手:

using System;
using System.Data;
using System.Web;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Context;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Mapping.ByCode;
using NHibernate.Tool.hbm2ddl;

namespace Gigastence.nHibernate.Web
{
    public static class Helper
    {
        #region Public Enums

        public enum DatabaseType
        {
            MSSQL2005,
            MSSQL2008,
            MySQL,
            MySQL5
        }

        #endregion

        #region Private Members

        private static HbmMapping _hbmMapping;
        private static Type[] _mappingTypes;
        private static Configuration _nHibernateConfiguration;

        #endregion

        #region Public Properties

        public static bool GenerateStatistics { get; set; }
        public static string ConnectionString { get; set; }

        public static ISession GetCurrentSession
        {
            get
            {
                //  Store the current ISession
                ISession tempSession = GetSessionFactory.GetCurrentSession();

                //  Return the current Session in for the relevant Context
                return tempSession.IsOpen ? tempSession : OpenSession();
            }
        }

        public static ISessionFactory GetSessionFactory { get; private set; }
        public static DatabaseType WorkingDatabaseType { get; set; }

        #endregion

        #region Private Methods

        private static void CompileSessionFactory()
        {
            //  ToDo: See if we can speed up this process by creating a static file to reference
            //
            //  Build the nHibernate Configuration and store it
            _nHibernateConfiguration = ConfigureNHibernate();

            //  Deserialize and Add the supplied Mappings to the nHibernate Configuration
            _nHibernateConfiguration.AddDeserializedMapping(_hbmMapping, null);

            //  ToDo: Figure out what this does!
            //
            SchemaMetadataUpdater.QuoteTableAndColumns(_nHibernateConfiguration);

            //  Create the Session Factory and store it
            GetSessionFactory = _nHibernateConfiguration.BuildSessionFactory();
        }

        private static Configuration ConfigureNHibernate()
        {
            //  Create a new nHibernate configuration
            var configure = new Configuration();

            //      ToDo: Allow for setting name of SessionFactory
            //
            //  Name the Session Factory
            configure.SessionFactoryName("Default");

            //  Wire up Session Factory Database component based on working database type
            switch (WorkingDatabaseType)
            {
                case DatabaseType.MySQL5:

                    //  !!! Is a MySQL 5 Database
                    configure.DataBaseIntegration(db =>
                    {
                        db.Dialect<MySQL5Dialect>();
                        db.Driver<MySqlDataDriver>();
                        db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
                        db.IsolationLevel = IsolationLevel.ReadCommitted;
                        db.ConnectionString = ConnectionString;
                        db.Timeout = 10;
                    });
                    break;
            }

            //  Generate Statistics, if required
            if (GenerateStatistics) configure.SessionFactory().GenerateStatistics();

            //  ToDo: Add modifier based on required Session Context
            //
            configure.CurrentSessionContext<ManagedWebSessionContext>();

            //  Return the Configuration
            return configure;
        }

        #endregion

        #region Public Methods

        public static void AddMapping(Type mappingType)
        {
            //  Determine if array is already available
            if (_mappingTypes == null)
            {
                //  Create a new array with one element
                _mappingTypes = new Type[1];
            }

            //  Copy existing array into a new array with one more element
            Array.Resize(ref _mappingTypes, _mappingTypes.Length + 1);

            //  Add the Mapping Type
            Array.Copy(new object[] { mappingType }, 0, _mappingTypes, _mappingTypes.Length - 1, 1);
        }

        public static void AddMappings(Type[] mappingTypes)
        {
            //  Iterate through passed types
            foreach(Type passedType in mappingTypes)
            {
                //  Add each typre
                AddMapping(passedType);
            }
        }

        public static void Application_BeginRequest()
        {
            //  Add the ISession object to the current request's Context for use throughout the request
            ManagedWebSessionContext.Bind(HttpContext.Current, OpenSession());
        }

        public static void Application_End()
        {
            //  Dispose of the Session Factory
            GetSessionFactory.Dispose();
        }

        public static void Application_EndRequest()
        {
            //  Unbind the Session from the request's context and place in the ISession holder
            ISession session = ManagedWebSessionContext.Unbind(HttpContext.Current, GetSessionFactory);

            //  Flush and Close the Session if it is still open
            if (session == null || !session.IsOpen) return;

            //  Proceed
            session.Flush();
            session.Close();
            session.Dispose();
        }

        public static void Application_Error()
        {
            //  Rollback transaction if there is one
            RollbackTransaction();
        }

        public static void BuildDatabase()
        {
            //  ToDo: Tidy Up and extend
            new SchemaExport(_nHibernateConfiguration).Execute(false, true, false);
        }

        public static void BuildSessionFactory(bool forceRebuild)
        {
            //  Determine if this is a forced rebuild
            if (forceRebuild)
            {
                //  !!! Forced rebuild

                //  Compile Session Factory
                CompileSessionFactory();
            }
            else
            {
                //  !!! Not a forced rebuild

                //  Reference the current Session Factory if available
                ISessionFactory sessionFactory = GetSessionFactory;

                //  Determine if Session Factory is built already
                if (sessionFactory == null)
                {
                    //  Compile Session Factory
                    CompileSessionFactory();
                }
            }
        }

        public static void CompileMappings(Type baseEntityToIgnore)
        {
            //  Using the built-in auto-mapper
            var mapper = new ModelMapper();

            //  Add prefetched Types to Mapper
            mapper.AddMappings(_mappingTypes);

            //  Compile the retrieved Types into the required Mapping
            _hbmMapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
        }

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

        public static void RollbackTransaction()
        {
            //  Get the current ISession from the request's context and place in the ISession holder
            ISession session = GetCurrentSession;

            //  Determine if the ISession exists
            if (session == null) return;

            //  It does, rollback current transaction if there is one
            if (session.Transaction.IsActive && !session.Transaction.WasRolledBack)
            {
                //  There was a transaction, rollback
                session.Transaction.Rollback();
            }
        }

        #endregion
    }
}

这就是我的使用方式:

using (ISession session = Helper.GetCurrentSession)
{
    using (ITransaction transaction = session.BeginTransaction())
    {
        //  Add data to the NHibernateSchemaVersions table
        var table = new NHibernateSchemaVersions { Component = "Web.Provider.Membership", Version = 1 };
        session.Save(table);

        transaction.Commit();
    }
}

到目前为止,我知道这是我应该做的,将工作包装在事务中。

我发现,由于 ISession 对象上的 USING 语句,会话在每次调用结束时关闭;这就是为什么我在 Helper.GetCurrentSession 调用中添加了检查它是否仍处于打开状态的逻辑。

我这样做对吗?我认为会话旨在保持活动状态直到请求结束,并确定它何时进入数据库。

我是否应该简单地获取会话并使用它而不是 USING 语句:

ISession session = Helper.GetCurrentSession;

using (ITransaction transaction = session.BeginTransaction())
{
    //  Add data to the NHibernateSchemaVersions table
    var table = new NHibernateSchemaVersions { Component = "Web.Provider.Membership", Version = 1 };
    session.Save(table);

    transaction.Commit();
}

如果是这样,我会在其他地方遇到问题吗?

感谢您的时间和帮助。

注意:我确实在寻找答案,但我无法将我的问题与任何事情联系起来。如果我错过了,请给我一个链接。

【问题讨论】:

    标签: c# asp.net nhibernate session


    【解决方案1】:

    我遇到了同样的问题。我现在在请求结束时明确地Dispose() 会话——问题解决了。

    由于我的库也用于非 Web 应用程序,因此我的帮助程序类和我的存储库提供了两个入口点——一个用于“持久”会话(显式关闭),另一个设计用于每次行程会话(隐式关闭using 语句)。

    “持久”会话的另一个很好的用途是,如果您在关系映射上使用延迟加载。如果您在检索“父”对象时自动关闭会话(通过using 语句),然后您尝试检索延迟加载属性,您将生成一个异常,因为会话它将尝试已关闭使用。

    更新

    根据要求,这是我目前的帮助类——我的非常基础。我的所有配置都在一个 XML 文件中。对于我的 MVC Web 应用程序,我正在通过调用 NHibernateHelper.GetPersistentSession().Dispose() 在 EndRequest 的侦听器中关闭 Global.asx 中的会话。

    public class NHibernateHelper
    {
        private static ISessionFactory _sessionFactory;
        private static ISessionFactory SessionFactory
        {
            get
            {
                if (_sessionFactory == null)
                {
                    var configuration = new NHibernate.Cfg.Configuration();
                    configuration.Configure();
                    configuration.AddAssembly(typeof(NHibernateHelper).Assembly);
                    _sessionFactory = configuration.BuildSessionFactory();
                }
                return _sessionFactory;
            }
        }
    
        public static ISession OpenSession()
        {
            return SessionFactory.OpenSession();
        }
    
        private static ISession _persistentSession = null;
        public static ISession GetPersistentSession()
        {
            if (_persistentSession == null)
            {
                _persistentSession = SessionFactory.OpenSession();
            }
    
            return _persistentSession;
        }
    }
    

    【讨论】:

    • 我想你可能误解了我的问题。虽然我会对您拥有的助手更感兴趣,但我的问题与“相同”请求中下一次使用之前的会话关闭有关,这是否会发生?提交事务时会话是否关闭?
    • 不,会话不会在提交时关闭。当您明确关闭或处置会话时,它会关闭。一个会话可以处理许多事务。
    • 谢谢布赖恩。 USING 语句是否关闭会话?也许这就是我出错的地方。
    • 是的,确实如此。 using 语句基本上将包含的代码包装在try . . . finallyfinally 块中,它调用using 语句中指定的项目的Dispose() 方法,在这种情况下是您的会话。基本上,如果您想在 HTTP 请求期间打开会话(我假设您的意思是),您必须放弃 using 语句(和明确的 Dispose)或找到一种方法将整个请求包装在 using 块中(不推荐)。
    • 谢谢,你证实了我的想法。除非必须,否则我不会再对会话使用 using 块;我的助手的 Application_EndRequest 为我处理会话。另一方面,厚颜无耻,你认为你能提供给我一个你的助手的样本吗?我通过例子学习,很简单,有很多例子我不知道哪个是正确的方法。不想提供也没关系;完全可以理解。
    猜你喜欢
    • 1970-01-01
    • 2012-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多