Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple SessionFactories in Windows Service with NHibernate

I have a Webapp which connects to 2 DBs (one core, the other is a logging DB).

I must now create a Windows service which will use the same business logic/Data access DLLs. However when I try to reference 2 session factories in the Service App and call the factory.GetCurrentSession() method, I get the error message "No session bound to current context".

Does anyone have a suggestion about how this can be done?

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();
    }
}  

The configuration file has the setting:

<property name="current_session_context_class">call</property>

The service sets up the factories:

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);

So finally, I try to call the correct factory by:

ISession session = StaticSessionManager.SessionFactory.GetCurrentSession();

Can anyone suggest a better way to handle this?
Thanks in advance!
Rob

like image 783
Rob Taylor Avatar asked May 28 '10 17:05

Rob Taylor


1 Answers

The first thing I can suggest is to make both your ISessionFactory instances as static. These should be singletons as they are very expensive to instantiate.

EDIT #1

Would you recommend I create the sessions when I need them, or leave them open?

The ISession API handles its connection internally. After a while, if no interaction with the underlying database is requested, the connection is closed, though your ISession keeps its connection instance. Once it needs to perform some other transactions with the database, it then reopens the connection previously used.

To answer your question, the prefered approach is is an instance of the ISession API per page (Web) or per Form (Desktop). For example, let's consider you have an accounting software, and the user has some management on the customers to do. Then, when your CustomerMgmtForm loads, you should provide an instance of ISession so that it can load your customers, track your changes, deletions and the new customers created (once joined to the ISession, so that when you call the SaveOrUpdate() method, the ISession knows what it has to do with the tracked changes and transient entities.

Why one instance per Page or per Form, do you wonder?

Since the ISession API keeps track of every changes occuring to an object, imagine once you have loaded your customers, suppliers, and some other entities you have to take care in your application. Each of the changes performed over customers, they has no right over the suppliers. But these customers will still be there, because it is the instance of ISession you have used for you customers! Then, your application's requirements for memory grows with the amount of objects loaded. Furthermore, it is a known issue that when the ISession grows too big in memory, it might cause some memory leaks, then considered as no more valid session by NHibernate, scrapping all of your unsaved changes and everything, as the session that tracked your entities is now invalid.

In addition to it, when you open let's say the CustomerMgmtForm, you will have to manage Customer entities. You are more likely to not having to keep track over your customers once you have closed the form, or even have opened the SuppliersMgmtForm, within which you shall manage your suppliers. By doing so, you will then have two instances of the ISession API: The first - customerMgmtSession, the other - suppliersMgmtSession. So, they should never grow too big to come to cause a memory leak, as they both have their own entities to handle or to take care. Both are totally independant of each other.

Following this approach, you should close and dispose of the instance of ISession API on FormClosing event for Windows Forms, or whatever equivalent in Web. So now, in a Windows Service, it is for you to decide what is the ideal situation and to decide where it would be the most appropriate for your needs, depending on what your service does.

One thing though, if your service does not require keeping any trace of your changes or whatsoever over your entities, the IStatelessSession API is perhaps more appropriate to use. Using it, then I for sure suggest that you open or instantiate a stateless session only when you need to interact with the underlying database, since there is no use for the IStatelessSession to be kept alive, as it doesn't provide the resources to keep any trace about the changes performed on your entities. This is the major and per haps only, if I remember correctly, difference between the ISession and the IStatelessSession APIs.

EDIT #2

Doing so, as above-mentioned in my EDIT #1, might also help you solve your problem, as you wouldn't ever need to call ISessionFactory.GetCurrentSession().

I hope this helps! =)

like image 171
Will Marcouiller Avatar answered Nov 02 '22 13:11

Will Marcouiller