我有一个连接到2个DB的Webapp(一个核心,另一个是日志记录DB)。
我现在必须创建一个使用相同业务逻辑/数据访问DLL的Windows服务。但是,当我尝试在服务应用程序中引用2个会话工厂并调用factory.GetCurrentSession()方法时,我收到错误消息“没有会话绑定到当前上下文”。
是否有人建议如何做到这一点?
public class StaticSessionManager
{
public static readonly ISessionFactory SessionFactory;
public static readonly ISessionFactory LoggingSessionFactory;
static StaticSessionManager()
{
string fileName = System.Configuration.ConfigurationSettings.AppSettings["DefaultNHihbernateConfigFile"];
string executingPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
fileName = executingPath + "\\" + fileName;
SessionFactory = cfg.Configure(fileName).BuildSessionFactory();
cfg = new Configuration();
fileName = System.Configuration.ConfigurationSettings.AppSettings["LoggingNHihbernateConfigFile"];
fileName = executingPath + "\\" + fileName;
LoggingSessionFactory = cfg.Configure(fileName).BuildSessionFactory();
}
}
配置文件具有以下设置:
<property name="current_session_context_class">call</property>
该服务设立工厂:
private ISession _session = null;
private ISession _loggingSession = null;
private ISessionFactory _sessionFactory = StaticSessionManager.SessionFactory;
private ISessionFactory _loggingSessionFactory = StaticSessionManager.LoggingSessionFactory;
...
_sessionFactory = StaticSessionManager.SessionFactory;
_loggingSessionFactory = StaticSessionManager.LoggingSessionFactory;
_session = _sessionFactory.OpenSession();
NHibernate.Context.CurrentSessionContext.Bind(_session);
_loggingSession = _loggingSessionFactory.OpenSession();
NHibernate.Context.CurrentSessionContext.Bind(_loggingSession);
最后,我尝试通过以下方式调用正确的工厂:
ISession session = StaticSessionManager.SessionFactory.GetCurrentSession();
有人能提出更好的方法来解决这个问题吗?
提前谢谢!
罗布
答案 0 :(得分:4)
我可以建议的第一件事是让你的ISessionFactory
个实例都是静态的。这些应该是单身,因为实例化它们非常昂贵。
编辑#1
您是否会建议我在需要时创建会话,或者让它们保持打开状态?
ISession
API在内部处理其连接。过了一会儿,如果没有请求与底层数据库的交互,则关闭连接,尽管ISession
保留其连接实例。一旦它需要与数据库执行一些其他事务,它就会重新打开以前使用的连接。
要回答您的问题,首选方法是每页ISession
API(Web)或每个表单(桌面)的实例。例如,让我们考虑您有一个会计软件,并且用户可以对客户进行一些管理。然后,当您CustomerMgmtForm
加载时,您应该提供ISession
的实例,以便它可以加载您的客户,跟踪您的更改,删除和创建的新客户(一旦加入ISession
,因此当您调用SaveOrUpdate()
方法时,ISession
知道它与跟踪的更改和瞬态实体有什么关系。
为什么每页或每个表单都有一个实例,你想知道吗?
由于ISession
API会跟踪对象发生的每个更改,因此想象一下,在加载了客户,供应商和其他一些实体后,您必须在应用程序中注意这些更改。每个变更都是针对客户进行的,他们对供应商没有任何权利。但是这些客户仍然会在那里,因为它是您用于客户的ISession
的实例!然后,应用程序对内存的要求随着加载的对象数量的增加而增长。此外,一个已知的问题是,当ISession
在内存中变得太大时,它可能会导致一些内存泄漏,然后被NHibernate视为不再有效的会话,废弃所有未保存的更改和所有内容,作为会话跟踪您的实体现在无效。
除此之外,当您打开让我们说 CustomerMgmtForm 时,您必须管理 Customer 实体。关闭表单后,您甚至无需跟踪客户,甚至打开 SuppliersMgmtForm ,您将在其中管理供应商。通过这样做,您将拥有ISession
API的两个实例:第一个 - customerMgmtSession
,另一个 - suppliersMgmtSession
。因此,它们永远不会变得太大而不会导致内存泄漏,因为它们都有自己的实体来处理或照顾。两者完全相互独立。
遵循这种方法,您应该关闭并处理Windows窗体的 FormClosing 事件上的ISession
API实例,或Web中的任何等效事件。现在,在Windows服务中,您可以根据自己的服务情况来决定最理想的情况,并根据您的需求决定哪种情况最合适。
但有一件事,如果您的服务不需要对您的实体保留任何更改或任何跟踪,IStatelessSession
API可能更适合使用。使用它,然后我肯定建议您只在需要与底层数据库交互时才打开或实例化无状态会话,因为IStatelessSession
没有用,因为它没有提供用于跟踪对您的实体执行的更改的任何跟踪的资源。如果我没记错的话,这只是主要的,也是唯一的,ISession
和IStatelessSession
API之间存在差异。
编辑#2
正如我在编辑#1 中提到的那样,这样做也可能有助于您解决问题,因为您无需拨打ISessionFactory.GetCurrentSession()
。
我希望这有帮助! =)