The point I leave the pattern, in previous post, was its implementation.
Before show how use it with AOP, I think is better to show how it work because my AOP implementation is only one of the possible implementations and you can write yours (and hopefully share it).
Introduction
In this post, you will see a persistent-conversation which theory was introduced in here. Remember that what I want do is abstract the persistent-conversation-handling from the DAO/Repository implementation. The persistence-layer, I’ll use, is NHibernate2.1.0 (trunk) and the UoW is the NHibernate’s session. The use of NH2.1.0 mean that you must know this, by the way in this example I’ll show how use the conversation avoiding any kind of DynamicProxy, IoC and AOP framework (I hope you would use one).
Even if the target of the example is a win-form or WPF application I don’t want spent a single second writing a GUI so you will see a console application.
The example core
To understand how use the pattern implementation you should study two implementations: PersistenceConversationalModel, FamilyCrudModel
Let me show only one method:
public IList<TAnimal> GetExistingComponentsList()
{
try
{
using (GetConversationCaregiver())
{
return animalDao.GetAll();
}
}
catch (Exception e)
{
ManageException(e);
throw;
}
}
The real action here is animalDao.GetAll(); . The action is enclosed in a “Resume” – “Pause”. In the try-catch I’m discarding the persistence conversation in case of an exception.
If you don’t use AOP you must enclose all DAOs/Repository actions with the same structure (obviously you can call more than one action between “Resume” – “Pause”).
The two methods to End or Abort the conversation are respectively:
public void AcceptAll()
{
try
{
EndPersistenceConversation();
}
catch (Exception e)
{
ManageException(e);
throw;
}
}
public void CancelAll()
{
try
{
AbortPersistenceConversation();
}
catch (Exception e)
{
ManageException(e);
throw;
}
}
From the point of view of Conversation-per-Business-Transaction usage that it’s all.
A more deep view
To have a more deep view, and understand what a possible AOP implementation must do (remember that you have one implemented and usable here), I must explain what are doing the PersistenceConversationCaregiver each time you call the method GetConversationCaregiver().
1: private class PersistenceConversationCaregiver : IDisposable
2: {3: private readonly ConversationEndMode endMode;
4: private readonly PersistenceConversationalModel pcm;
5: 6: public PersistenceConversationCaregiver(PersistenceConversationalModel pcm, ConversationEndMode endMode)
7: {8: this.pcm = pcm;
9: this.endMode = endMode;
10: string convId = pcm.GetConvesationId();
11: IConversation c = pcm.cca.Container.Get(convId) ?? pcm.cf.CreateConversation(convId); 12: pcm.cca.Container.SetAsCurrent(c); 13: c.Resume(); 14: } 15: 16: #region Implementation of IDisposable
17: 18: public void Dispose()
19: { 20: IConversation c = pcm.cca.Container.Get(pcm.GetConvesationId());21: switch (endMode)
22: {23: case ConversationEndMode.End:
24: c.End();25: break;
26: case ConversationEndMode.Abort:
27: c.Dispose();28: break;
29: default:
30: c.Pause();31: break;
32: } 33: } 34: 35: #endregion
36: }In the constructor (form line 6 to 14):
line 10 : I’m getting the conversationId from the “model” instance. The conversationId is a Guid in a string and the “model” is the conversationId-holder.
line 11: I’m getting the existing conversation, from the container, or I create a new one.
line 12: I’m setting the conversation as the current conversation.
line 13: I’m starting or resume the conversation.
In the dispose (from line 18 to 33):
line 20: I’m getting the conversation (it must exists)
line 30: I’m pausing the conversation.
To End or Abort the conversation the implementation is:
protected void EndPersistenceConversation()
{
IConversation c = cca.Container.Get(GetConvesationId());
if(c!=null)
{
c.Resume();
c.End();
}
}
protected void AbortPersistenceConversation()
{
IConversation c = cca.Container.Get(GetConvesationId());
if (c != null)
{
c.Abort();
}
}
Note the Resume before the End of the conversation; to be ended the conversation must be active.
The GetConvesationId() have no secrets:
protected virtual string GetConvesationId()
{
if (conversationId == null)
{
conversationId = Guid.NewGuid().ToString();
}
return conversationId;
}
What is more important, perhaps, is the ManageException method:
protected virtual void ManageException(Exception e)
{
IConversation c = cca.Container.Unbind(conversationId);
if (c != null)
{
c.Dispose();
}
}
Here I’m unbinding the conversation from the container and disposing the conversation. In term of NHibernate this mean the Rollback of the transaction and the dispose of the session. After this operation you can continue working in the same “Model” instance, what you must know is that you will use a new NHibernate-session.
The Example
The example show how the pattern work in tow cases:
- A simulation of an input in one Form
- A simulation of an input in two opened Form
If you download the code you can see a Diagram in each “layer”.
To have fun, writing the example, I have implemented a HomeMadeServiceLocator and I hope it can be useful to who are scared by IoC, Dependency Injection and AOP words.
The code of the example is available here.
If you have some question, about the example, feel free to ask.
3 comentarios:
Fabio,
I wrote a post on my take on Conversation per Business transaction could you please look over it and let me know what you think or see if you can spot anything wrong?
http://dotnetchris.wordpress.com/2009/01/27/conversation-per-business-transaction-using-postsharp-and-ioc/
Thanks!
Chris Marisic
Fabio
I really want to take a look at your example, but I can't find a way to download the sourcecode. Can you help me please?
Thanks!
Koen
To download sources you can use TortoiseSvn and then take a look to
http://code.google.com/p/unhaddins/source/checkout
Post a Comment