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

发布于 2024-11-03 01:02:06 字数 3426 浏览 3 评论 0原文

因此,基于这个问题(此处),我最后问了这个问题一周,我决定去看看 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);
        }
    }
}

So based on this question (here), of which I asked last week, I decided to go and have a look into the Castle project and use the Castle.Facilities.NHibernateIntegration facility.

I spent the best part of two days messing around with it and have come to the same issue: NHibernate Thread-Safe Sessions. I was hoping, out of the box, the built in ISessionManager was smart enough to handle threading, which is the reason why I decided to implement it.

In the very sparse documentation on that particular project it mentions that calling ISessionManager.OpenSession is much the same as calling session.GetCurrentSession. From this I gather there is no way for me to, force open, a new seperate session.

So has anyone the solution for me or any ideas how I can work with this issue?

(I know most people are going to say only work with one thread, but honestly think outside the box, some tools and routines automatically spawn a new thread. For instance, log4net and sessionstatestore. You can't just assume there will only be one thread, associated, with the current request.)

Notes:

  • I'm working on the web model with .NET 4 web application.

  • I invoke and resolve the Windsor container in the usual, documented way and let the container resolve the session manager. I do this in both threads.

  • Here is my Castle NHibernate config:

Code:

<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>
  • Here is my example DAO

Code:

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();
            }
        }
    }
}
  • And how I call the DAO. Note: This is on the newly spawned thread which is out of my hands.

Code:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

最偏执的依靠 2024-11-10 01:02:06

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

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

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

If you want full control of the session, I believe that the NHibernateFacility actually registers the underlying ISessionFactory to the Windsor kernel.

From that, you can invoke sessionFactory.OpenSession() which I think should always return a new session.

I honestly don't really see what ISessionManager brings to the party...

極樂鬼 2024-11-10 01:02:06

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

它可能会解决您的多线程问题,因为它与以前的设施的意图不同;这个可以让您为每个事务保留一个会话,而不是为每个请求保留一个会话。这样,就避免了多线程问题,并且它在您的附加程序中同样可以很好地工作。

在代码中,这是因为 .Net 有一个 CallContext 静态类,它知道您所在的线程(但将其绑定到您的调用上下文而不是线程静态)。

Have a look at this link!
https://github.com/haf/Castle.Facilities.NHibernate/wiki

It might solve your multi-threaded problems, as it differs in intention to the previous facility; this one lets you keep a session-per-transaction rather than one per request. As such, the multi-threaded issue is avoided and it would work equally well from your appender.

In the code, this is because the .Net has a CallContext static class that knows about what thread you're on (but ties it to your call context rather than thread-static).

°如果伤别离去 2024-11-10 01:02:06

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

诀窍就如济世所说;您需要访问 ISessionFactory,而不是从 FuncISessionManager 中提取会话。

不幸的是,对我来说,这并不像通过构造函数注入它并让 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。

We were running into this problem a lot when using the SessionPerWebRequest pattern and then forking worker threads, which as you say, cannot be helped in some situations.

The trick is as jishi says; instead of pulling the session from Func<ISession> or ISessionManager, you need to get access to ISessionFactory.

Unfortunately for me, this wasn't as simple as injecting it through the constructor and having Windsor resolve it - it isn't registered as part of the installer as jishi said (unless I'm missing something). However, it is accessible through an installer callback:

public class NHibernateInstaller : INHibernateInstaller, IDatabaseInstaller
{
    ...

    public void Registered(ISessionFactory factory)
    {
        SessionFactoryStore.Set(SessionFactoryKey, factory);
    }
}

Where SessionFactoryStore is a singleton repository for storing your factories (in the case where you may have multiple factories, spread across clients, like me).

[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;
    }
}

Then wherever you implement your unit of work pattern, or similar, just perform a test to see if you are running in a normal, or threaded state:

[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, thread-safe ISession using NHibernateIntegration.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文