Web 上下文中 Castle.Facilities.NHibernateIntegration ISessionManager 的线程安全问题
因此,基于这个问题(此处),我最后问了这个问题一周,我决定去看看 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果你想完全控制会话,我相信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...
看看这个链接!
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).
当使用 SessionPerWebRequest 模式然后分叉工作线程时,我们经常遇到这个问题,正如您所说,这在某些情况下无济于事。
诀窍就如济世所说;您需要访问
ISessionFactory
,而不是从Func
或ISessionManager
中提取会话。不幸的是,对我来说,这并不像通过构造函数注入它并让 Windsor 解析它那么简单 - 它没有像 jishi 所说的那样注册为安装程序的一部分(除非我遗漏了一些东西)。但是,可以通过安装程序回调访问它:
其中 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>
orISessionManager
, you need to get access toISessionFactory
.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:
Where SessionFactoryStore is a singleton repository for storing your factories (in the case where you may have multiple factories, spread across clients, like me).
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:
Hey presto, thread-safe ISession using NHibernateIntegration.