【问题标题】:Thread safe issue with Castle.Facilities.NHibernateIntegration ISessionManager in Web contextWeb 上下文中 Castle.Facilities.NHibernateIntegration ISessionManager 的线程安全问题
【发布时间】:2013-12-19 00:25:21
【问题描述】:

因此,基于我上周提出的这个问题 (here),我决定去看看 Castle 项目并使用 Castle.Facilities.NHibernateIntegration 设施。

我花了两天时间弄乱它并遇到了同样的问题:NHibernate Thread-Safe Sessions。我希望开箱即用的内置 ISessionManager 足够智能以处理线程,这就是我决定实现它的原因。

在关于该特定项目的非常稀疏的文档中,它提到调用 ISessionManager.OpenSession 与调用 session.GetCurrentSession 大致相同。据此,我认为我无法强制打开一个新的单独会话。

那么有没有人为我提供解决方案或任何想法我可以如何解决这个问题?

(我知道大多数人会说只使用一个线程,但老实说,跳出框框思考,一些工具和例程会自动生成一个新线程。例如,log4net 和 sessionstatestore。你不能只是假设会有只有一个线程,关联,与当前请求。)

注意事项:

  • 我正在使用 .NET 4 Web 应用程序开发 Web 模型。

  • 我以通常的记录方式调用和解析 Windsor 容器,并让容器解析会话管理器。我在两个线程中都这样做。

  • 这是我的 Castle NHibernate 配置:

代码:

<facility id="nhibernate" isWeb="true" type="Castle.Facilities.NHibernateIntegration.NHibernateFacility, Castle.Facilities.NHibernateIntegration">
  <factory id="nhibernate.factory">
    <settings>
      <item key="connection.connection_string">#{NHibernateConnectionString}</item>
      <item key="connection.driver_class">#{NHibernateDriver}</item>
      <item key="connection.provider">NHibernate.Connection.DriverConnectionProvider</item>
      <item key="dialect">#{NHibernateDialect}</item>
      <item key="generate_statistics">true</item>
      <item key="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</item>
      <item key="show_sql">true</item>
    </settings>
    <assemblies>
      <assembly>Gigastence.Base.Common</assembly>
    </assemblies>
  </factory>
  • 这是我的 DAO 示例

代码:

public class NHibernateDao : INHibernateDao
{
    private ISessionManager sessionManager;

    public NHibernateDao(ISessionManager sessionManager)
    {
        this.sessionManager = sessionManager;
    }

    public void Append(LoggingEvent loggingEvent)
    {
        using (IStatelessSession session = sessionManager.OpenStatelessSession())
        {
            using (ITransaction tran = session.BeginTransaction())
            {
                Log data = new Log
                {
                    Id = Guid.NewGuid(),
                    Date = loggingEvent.TimeStamp,
                    Level = loggingEvent.Level.ToString(),
                    Logger = loggingEvent.LoggerName,
                    Thread = loggingEvent.ThreadName,
                    Message = loggingEvent.MessageObject.ToString()
                };

                if (loggingEvent.ExceptionObject != null)
                {
                    data.Exception = loggingEvent.ExceptionObject.ToString();
                }

                session.Insert(data);
                tran.Commit();
            }
        }
    }
}
  • 以及我如何称呼 DAO。 注意:这是在我无法控制的新产生的线程上。

代码:

public class NHibenateAppender : AppenderSkeleton
{
    protected override void Append(LoggingEvent loggingEvent)
    {
        if(IoC.IsInitialized)
        {
            var NHibernateLogger = IoC.Resolve<INHibernateDao>();
            NHibernateLogger.Append(loggingEvent);
        }
    }
}

【问题讨论】:

    标签: multithreading nhibernate thread-safety castle-windsor castle


    【解决方案1】:

    如果您想完全控制会话,我相信 NHibernateFacility 实际上将底层 ISessionFactory 注册到 Windsor 内核。

    从此,您可以调用sessionFactory.OpenSession(),我认为它应该总是返回一个新会话。

    老实说,我真的不明白 ISessionManager 给聚会带来了什么...

    【讨论】:

    • 其实我也没想到,我相信你是对的。我得去看看。至于 ISessionManager,我确信它最终会有所作为,^^ 但是什么,没有文档可以弄清楚是什么 - 只是一个 api 参考。
    • 它带来了对多个数据库的控制、ISession 生活方式的处理、与 LTM 的事务集成等......
    • Henrik - 意思是您可以即时修改 ISession Lifestyle?
    【解决方案2】:

    看看这个链接! https://github.com/haf/Castle.Facilities.NHibernate/wiki

    它可能会解决您的多线程问题,因为它的意图与以前的工具不同;这让您可以为每个事务保留一个会话,而不是每个请求一个。因此,避免了多线程问题,并且在您的 appender 中同样可以正常工作。

    在代码中,这是因为 .Net 有一个 CallContext 静态类,它知道你在哪个线程上(但它与你的调用上下文相关联,而不是线程静态的)。

    【讨论】:

      【解决方案3】:

      在使用 SessionPerWebRequest 模式然后分叉工作线程时,我们经常遇到这个问题,正如您所说,在某些情况下无法解决。

      诀窍正如吉时所说;而不是从Func&lt;ISession&gt;ISessionManager 拉会话,您需要访问ISessionFactory

      对我来说不幸的是,这并不像通过构造函数注入它并让 Windsor 解决它那么简单——它没有像 jishi 所说的那样注册为安装程序的一部分(除非我遗漏了一些东西)。但是,它可以通过安装程序回调访问:

      public class NHibernateInstaller : INHibernateInstaller, IDatabaseInstaller
      {
          ...
      
          public void Registered(ISessionFactory factory)
          {
              SessionFactoryStore.Set(SessionFactoryKey, factory);
          }
      }
      

      其中 SessionFactoryStore 是用于存储工厂的单例存储库(在您可能有多个工厂的情况下,分布在客户端上,比如我)。

      [Singleton]
      public class SessionFactoryStore: ISessionFactoryStore
      {
          Dictionary<string, ISessionFactory> SessionFactories { get; set; }
      
          public SessionFactoryStore()
          {
              SessionFactories = new Dictionary<string, ISessionFactory>();
          }
      
          public void Set(string key, ISessionFactory factory)
          {
              lock (SessionFactories)
              {
                  if (!SessionFactories.ContainsKey(key)) SessionFactories.Add(key, factory);
              }
          }
      
          public ISessionFactory Get(string key)
          {
              return SessionFactories.ContainsKey(key) ? SessionFactories[key] : null;
          }
      }
      

      然后,无论您在何处实施工作单元模式或类似模式,只需执行测试以查看您是在正常状态还是线程状态下运行:

      [PerWebRequest]
      public class UnitOfWork : IUnitOfWork
      {
          private IGenericFactory GenericFactory { get; set; }
      
          private Func<ISession> Session { get; set; }
          private ISessionFactoryStore SessionFactoryStore { get; set; }
      
          private ISession GetSession(bool isThreaded)
          {
              if (!isThreaded)
                  return Session();
              else
                  return SessionFactoryStore.Get("YourFactoryKey").OpenSession();
          }
      
          public UnitOfWork(Func<ISession> session, ISessionFactoryStore sessionFactoryStore) {
              Session = session;
              SessionFactoryStore = sessionFactoryStore;
          }
      
          ...
      }
      

      你好,使用 NHibernateIntegration 的线程安全 ISession。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-04-30
        • 2013-08-16
        • 2021-12-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-13
        • 2010-10-08
        相关资源
        最近更新 更多