Burrow Conversation is the core concept of NHibernate.Burrow. It was inspired by the Conversation concept in JBoss Seam .
The concept Conversation is to represent a stateful business transaction between end-user and system. Burrow manages NH session and transaction(optional) around conversation - it opens a session at the beginning of each conversation, and then close the session at the end of it (note: transaction strategy will be discussed later).
Burrow Conversation can be a long conversation between user and system - it can span over multiple http requests. In another sentence, Burrow Conversation allows end user to have a stateful business transaction with the system that spans over multiple http requests and responses. A common example of such long conversation is a checkout process - user enters shipping information, billing information and confirm the order information in multiple steps.
More importantly, like in Seam, one user can have multiple Burrow Conversations with the system simultaneously. For example, user can use one browser window/tab to checkout his shopping cart and another browser window/tab to process an order return and another browser window/tab to modify his/her account information. This feature is not only beneficial but also required because it's very hard for the web application to stop user from opening multiple browser window/tabs interacting with the system, which could cause serious concurrency problem if multi-conversation is not supported (This could happen if you simple store NHibernate Session in HttpSession to support long Session).
By default, Burrow framework starts a conversation at the beginning of a request and commit/close it at the end of the request - we call it a short conversation. In terms of NH session management, it can be deemed as an implementation of the OpenSessionInView pattern, that is, one NHibernate session and one transaction per http request. This takes care of most business transactions that require only one http request, for example, modifying the customer information. Burrow takes care of this type of short conversation transparently - your code does not even need to be aware of Burrow Conversation.
If you need to have a long conversation, you need to know a little about the NHibernate.Burrow.IConversation interface. We are going to explain it here.
In the request that you are going to start a long conversation - for example when you start a checkout process - you need to ask the current Burrow Conversation to span. Here is how:
1: //Get a facade instance of burrow, it's free
2: BurrowFramework bf = new BurrowFramework();
3:
4: //ask the current IConversation to span with PostBacks
5: bf.CurrentConversation.SpanWithPostBacks(TransactionStrategy.BusinessTransaction);
bf.CurrentConversation gives you the current IConversation (the interface represents Burrow Conversation) you are in. It was a short conversation before. IConversation.SpanWithPostBacks() tells the conversation to span with the post backs of a form. Now it becomes a long conversation. In pages that have only one form, this means that the conversation will be spanning as long as the user stays in the same page - the following postback requests will be using the same IConversation as this one. If the user goes to another page or close the current browser window/tab, the conversation will be discarded (How to span the conversation to another page is explained later in the article).
After the long business transaction is done - for example, the user finally confirmed the placement of the order in the checkout - you need to tell the Long Conversation to stop spanning (BTW, it's not needed for short conversation). Here is how:
bf.CurrentConversation.FinishSpan(); // note: bf is the BurrowFramework instance. you can create it anywhere.
Burrow will commit the conversation at the end of this request - it will close the conversation. The next request will be assigned with a new conversation.
Also, you can cancel the conversation in the middle of it - for example user clicks the cancel button or something goes wrong. Here is how:
bf.CurrentConversation.GiveUp(); // note: bf is the BurrowFramework instance. you can create it anywhere.
The conversation won't be discarded immediately, but Burrow will rollback the transaction and close the conversation at the end of this request. This way all the data changes in this conversation won't be committed to database, but you can still read data from Database in this request - for example to display some user friendly error message. If something quite fatal happens and you really want to immediately cancel the current conversation, you can either throw an exception or do the following:
bf.CurrentConversation.GiveUp(); // note: bf is the BurrowFramework instance. you can create it anywhere.
bf.CloseWorkSpace();
After you called bf.CloseWorkSpace(), you'll probably need to immediately redirect to another page as Burrow managed services are no longer available for this request.
I know you must have noticed the TransactionStrategy parameter we passed to SpanWithPostBacks() method, this parameter tells Burrow how to manage transactions for this long conversation. There are three of them for you to choose.
Other than these three transaction strategies, you can set Burrow to leave the transaction to your own management, that is Burrow will no longer manage the transaction, you can and need to use BurrowFramework.CurrentConversation.TransactionManager to manage it. To use manual transaction management, set manualTransactionManagement="true" in the NHibernate.Burrow Section
<NHibernate.Burrow manualTransactionManagement="true" >
...
</NHibernate.Burrow>
That is the handling for most long conversation scenarios. The following are a couple of special cases:
Span by URL
If you need the long conversation to span to another page, you still call SpanWithPostBack() method, but then you'll need to wrap the url of that page with conversation information so that Burrow will pick up the conversation for it. Here is how:
WebUtil wu = new WebUtil();
//use the following url instead of "~/anotherPage.aspx"
string urlToAnotherPage = wu.WrapUrlWithConversationInfo("~/anotherPage.aspx");
If you need a link on your page, you can either use this API to wrap the link, or you can use the NHibernate.Burrow.WebUtil.Controls.BurrowLink control. It inherits from HyperLink so you can use it exactly the same as HyperLink but it will automatically wrap the conversation information for you.
Span by Cookie
Another way to span a conversation is IConverastion.SpanWithCookie(string workSpaceName); This will store the conversation information into browser cookies. However not all pages that picks up that cookie will pick up that conversation, which is unmanageable and dangerous. The concept WorkSpaceName is introduced to help here. You can group your HttpHandlers using this WorkSpaceName - HttpHandlers defined with the same WorkSpaceName can be deemed as being in the same WorkSpace. When you call CurrentConversation.SpanWithCookie(string workSpaceName) only the HttpHandlers defined with the same workSpaceName will be sharing that conversation. Here is an example of how it works:
Define the WorkSpaceName for APageInWorkSpaceA.aspx:
[NHibernate.Burrow.WebUtil.Attributes.WorkSpaceInfo("WorkSpace1")] //Define the page's workspace using Attribute
public partial class SharingConversations_Step06b : System.Web.UI.Page
{}
Start spanning in another page:
bf.CurrentConversation.SpanWithCookie("WorkSpace1");
Now if user opens APageInWorkSpaceA.aspx in the same browser session, the request will be using the same conversation. As for Finish or GiveUp, there is no difference from long conversation spanning with postbacks.
If attribute is not enough for defining page's workSpaceName, as it's static, you can create your own Handler-WorkSpace management mechanism and implement the IWorkSpaceNameSniffer interface in Web.Util, that interface is in charge of getting the WorkSpaceName of an IHttpHandler. You can set in the Burrow Configuration the type of implementation of that interface the system should use ( Please look at NHibernate.Burrow.IBurrowConfig ).