Web上下文中Castle.Facilities.NHibernateIntegration ISessionManager的线程安全问题

时间:2011-04-25 20:08:11

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

基于上周我问过的这个问题(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);
        }
    }
}

3 个答案:

答案 0 :(得分:0)

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

从那里,你可以调用我认为应该总是返回新会话的sessionFactory.OpenSession()

老实说,我真的没有看到ISessionManager带给派对的东西......

答案 1 :(得分:0)

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

它可以解决您的多线程问题,因为它与先前设施的意图不同;这个允许你保持每个事务的会话而不是每个请求一个。因此,避免了多线程问题,并且它可以从你的appender同样很好地工作。

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

答案 2 :(得分:0)

我们在使用SessionPerWebRequest模式然后分叉工作线程时遇到了很多问题,正如你所说,在某些情况下无法帮助。

诀窍就像吉士所说的那样;而不是从Func<ISession>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;
    }

    ...
}

Hey presto,使用NHibernateIntegration进行线程安全的ISession。