Try fast search NHibernate

22 December 2008

Implementing Conversation per Business Transaction

In the previous post I talked about the theory behind the pattern. This post is about its implementation.

Introduction

If you read my blog you know how much I like “Program to an interface and not to an implementation”; this mean that first of all you will see interfaces instead of classes.
One of the targets of this implementation is maintain the same style of the others “Session Easier” of uNhAddIns; this mean:

The big picture

IConversation
IConversation
Is a representation of a “Persistence conversation” I showed in the previous post. In addition there are some events.
ConversationException
Is the base exception for “all” conversation implementations.
IConversationFactory
So far I “don’t know” which will be the real implementation of the “Persistence conversation” (may be ADO.NET, may be NHibernate); its factory is needed.
IConversationContainer
As I said, in a IM, we may have more than one conversation happening at the same time. This fact still true for “Persistence conversation”. For a winForm application it is pretty easy to understand because, in the same application (mean same Thread), the user may activate more than one independent business-transaction. In a WEB application it still true because the user may open more than one browser-tab, in the same browser instance sharing the same HttpSession, activating various independent business-transaction (or better he sure hope that each tab are working in a independent persistence context).
IConversationsContainerAccessor
In my application I need something to access to the ConversationContainer instance (a sort of a specific ServiceLocator for ConversationContainer implementation) especially if I want allow the use of a formal DI container.

The implementation

ConversationImpl1
AbstractConversation
Encapsulation of the behavior of a generic Persistence conversation. The implementation, basically, define that each conversation have an ID defined at the moment of its creation, implements the EqualityComparer based on the ID, implements the Disposable pattern and implements the events managements. The real hard work will be done in the five abstract methods: DoStart, DoPause, DoResume, DoEnd, DoAbort.
NhConversation
At the end, I have arrived to the Persistence Conversation implemented for NHibernate. The base behavior was described at the end of the previous post, what I would explain here is the role of the two injected fields: factoriesProvider, wrapper.
The factoriesProvider is an implementation of ISessionFactoryProvider that is the class responsible to provide all ISessionFactory needed by our application. In uNhAddIns you have two available implementations to work with one or more than one RDBMS, respectively SessionFactoryProvider and MultiSessionFactoryProvider. In the MultiSessionFactoryProvider you can inject an instance of IMultiFactoryConfigurator or use the default implementation.
The wrapper is an implementation of ISessionWrapper. The main target of a wrapped session is the interception of the Close and Dispose. In uNhAddIns the base implementation are doing something more: it are ensuring that you are working applying a best practice for session&transaction management. The implementation of ISessionWrapper is the responsible to wrap a session and recognize a wrapped instance. In uNhAddIns, so far, you have two available implementations using Castle.DynamicProxy2 and LinFu.DynamicProxy. Obviously you can write your own implementation without transaction protection.
NHibernate Conversation solved, now the implementation of the others interfaces to work with Conversation-per-Business-Transaction pattern.
ConversationImpl2
DefaultConversationFactory
Nothing special to say, its implementation is trivial.
AbstractConversationContainer
Encapsulation of the behavior of the conversation container. What I’m not defining here is which will be the real context where the container are running, that mean where all conversation will be stored and where will be stored the current conversation id. The Store of conversation is a Dictionary<string, IConversation> where the key is the ConversationId and the value is an instance of an started conversation.
ThreadLocalConversationContainer
This is the implementation of the ConversationContainer for a winForm, or WPF, application. As you can see the CurrentId and the Store are two ThreadStatic fields.
ThreadLocalConversationalSessionContext
As I said at the begin of this post the “story” end when I have an implementation of ICurrentSessionContext. The implementation, at this point, is trivial but the advantage of an implementation of ICurrentSessionContext is really big:
  • Your DAOs/Repositories are wired only with NHibernate and nothing more than its SessionFactory.
  • You can change the strategy of session-handling without change absolutely nothing in your DAOs/Repositories
To be clear, about the advantage of an implementation of ICurrentSessionContext, take a look on how may appear an implementation of a simple DAO
public class SillyDao : ISillyDao
{
private readonly ISessionFactory factory;

public SillyDao(ISessionFactory factory)
{
this.factory = factory;
}

public Silly Get(int id)
{
return factory.GetCurrentSession().Get<Silly>(id);
}

public IList<Silly> GetAll()
{
return factory.GetCurrentSession().CreateQuery("from Silly").List<Silly>();
}

public Silly MakePersistent(Silly entity)
{
factory.GetCurrentSession().SaveOrUpdate(entity);
return entity;
}

public void MakeTransient(Silly entity)
{
factory.GetCurrentSession().Delete(entity);
}
}

As you can see I’m using factory.GetCurrentSession() this mean that my DAOs don’t know who and how the session is provided; don’t know nothing about uNhAddIns, don’t know nothing about conversation management.

Conclusion

The next step, perhaps before a working example, will be “Aspect Conversation-per-BusinessTransaction” to have the “Full cream” working together. If you are inpatient you can see it by your self.


kick it on DotNetKicks.com


13 comments:

  1. >my DAOs don’t know who and how the session is provided;
    Hello, Fabio.
    This is very similar to the implementation of Spring.Data.NHibernate.

    ReplyDelete
  2. Well... a good nh-session-handler should implements ICurrentSessionContext (for some reason we have it in NHibernate ;) )

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. SpringSessionContext implements
    ICurrentSessionContext.

    /// Implementation of NHibernates 1.2's ICurrentSessionContext interface
    /// that delegates to Spring's SessionFactoryUtils for providing a
    /// Spirng-managed current Session.


    public class SpringSessionContext : ICurrentSessionContext

    ReplyDelete
  5. As I said, a good nh-session-handler should implements ICurrentSessionContext ;)
    In uNhAddIns you can find some other "standard" context for session-per-request, session-per-conversation in WEB and a "manual-managed" ThreadLocal context for Test/WinForm. Conversation-per-BusinessTransaction is a more formal concept to manage a Persistence-Conversation.

    ReplyDelete
  6. Thanks for reply. I would say that Spring.NET have similar opportunities:SessionScope, OpenSessionInViewModule and so on in Spring.Data.NHibernate namespace. ))

    ReplyDelete
  7. I'll check it. Probably the difference here will be that Spring will be an option as Castle, LinFu, Ninject, PostSharp only for the last implementation... matter of the next post.

    ReplyDelete
  8. My repository classes require an ISession in the constructor. That's my session-per-conversation implementation. It's not clear to me why all this additional complexity is needed.

    ReplyDelete
  9. @Jamie
    This implementation are showing how implement what is behind your repository.
    From where come the session you are injecting ?
    This implementation is "From where come".
    BTW, as I write in the introduction of this post, one of the target is the implementation of ICurrentSessionContext that is another best-practice (you can read about it in NHiA).
    If you are injecting the ISession instead the ISessionFactory, in your repository, you have less accessibility to NH available features.

    ReplyDelete
  10. @Fabio
    I have a small assembly that stores configures session factories (one for each db) and stores them in a dictionary in a thread-safe lazy singleton. The current session is stored in a static member in global.asax for ASP.NET apps where I use session-per-request. I haven't converted our Winforms app yet but I expect that the current session will generally belong to the form that initiates it.

    What ISessionFactory features might I want to use in a repository?

    To be honest, NHiA is so long-delayed (and now refers to an older version) that it's hard to take its recommendations seriously. I have HiA but I haven't read it all. I will look into ICurrentSessionContext, thanks for the pointer.

    ReplyDelete
  11. In uNhAddIns the single-DB or multi-DB stuff are transparent for all session-handlers available in uNhAddIns.
    If you want take a look, classes involved are:
    -Implementations of ISessionFactoryProvider
    -Implementations of IMultiFactoryConfigurator

    ReplyDelete
  12. Ups... I forgot your question...
    example 1: you would need a StatelessSession instead a ISession.
    example 2: you would need a separate new fresh ISession instance instead the session managed from your session-handler.

    Many concepts exposed in NHiA are valid for NH2.0.1 too.

    ReplyDelete
  13. @Fabio

    Could you please explain in more details about how to configure the "uNHAddins.Examples.SessionManagement" so that it works with 2 databases? Thanks.

    I did the following:
    1. in file uNhAddIns__default.config
    change SessionFactoryProvider --> MultiSessionFactoryProvider

    2. add 2 appSettings as below in app.config
    add key="nhfactory.A" value="A.cfg.xml"
    add key="nhfactory.B" value="B.cfg.xml"

    3. create the 2 files
    A.cfg.xml
    B.cfg.xml

    4.What am I to do if I need ProductRepository class to use Factory A and OrderRepository to use Factory B ?

    Thank you so much

    ReplyDelete