基于上周我问过的这个问题(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>
代码:
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();
}
}
}
}
代码:
public class NHibenateAppender : AppenderSkeleton
{
protected override void Append(LoggingEvent loggingEvent)
{
if(IoC.IsInitialized)
{
var NHibernateLogger = IoC.Resolve<INHibernateDao>();
NHibernateLogger.Append(loggingEvent);
}
}
}
答案 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。