Burrow is a light weight middleware developed to support .Net applications using NHibernate (maybe also referred as NH in this article) as ORM framework.
Using Asp.net with NHibernate could be a challenge because of the fact that NHibernate is a stateful environment while Asp.net is a stateless framework. Burrow can help solve this conflict by providing advanced and smart session/transaction management and other facilitates.
The core concept of Burrow is Burrow Conversation, which was inspired by the Conversation concept in JBoss Seam . Conversation is designed to represent a stateful business conversation between end-user and system. Burrow manages NH session around conversation - it opens a session at the beginning of each conversation, and then close the session at the end of the conversation.
For business transactions that can be taken care of in one http request response round trip, for example, modifying the customer information, Burrow takes care of the conversation transparently, which means that your code does not need to be aware of Burrow conversation. By default, Burrow framework starts a conversation at the beginning of a request and commit/close it at the end of the request. 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.
OpenSessionInView pattern isn't hard to implement, but the real power Burrow provides is that Burrow conversation can span over multiple http requests, in another sentence, Burrow Conversation allows end user to have a stateful business conversation with the system that spans over multiple http request and response. For example the checkout process is a typical business transaction that can span over multiple request/response dialogues - user needs to enter shipping information, billing information and confirm the order information. Through conversation, Burrow can also make sure that such long business transaction is atomic and isolated by applying atomic transaction strategy. More importantly, like in Seam, one user can have multiple Burrow conversations with the system simultaneously.
For more information about Burrow Conversation, click here.
To demonstrate Burrow, we used the basic example from the well-known article "NHibernate Best Practices with ASP.NET" from CodeProject.com. We have two versions of modifications of it:
Most of the code in BasicCore example is the same as the original basic sample to demonstrate that Burrow can be used with any design and architecture. We added a PlaceOrder.aspx to demonstrate what Burrow can enable - business transaction that spans over multiple requests. This page is like a checkout page, but tweaked for demo purpose. The steps are simple as the point here is to demonstrate the multiple steps.
PlaceOrder.aspx
<asp:PlaceHolder ID="phSelectCustomer" runat="server" Visible="true">
Select a customer who wants to place an order (will be the OrderBy)<br />
<asp:DropDownList ID="ddlCustomers" runat="server" DataTextField="ContactName" DataValueField="ID">
</asp:DropDownList>
<asp:Button ID="btnSelectCustomer" runat="server" Text="Next" OnClick="Step1" />
</asp:PlaceHolder>
<asp:PlaceHolder ID="phEnterShipToName" runat="server" Visible="false">
Please enter your ship to information:
<asp:TextBox ID="tbShipToName" runat="server"></asp:TextBox>
<asp:Button ID="btnEnterShipTo" runat="server" Text="Next" OnClick="btnStep2" /><br />
<asp:Button ID="btnCancel" OnClick="Cancel" runat="server" Text="cancel"></asp:Button>
</asp:PlaceHolder>
<asp:PlaceHolder ID="phConfirm" runat="server" Visible="false">Customer:
<asp:Literal ID="lCustomer" runat="server"></asp:Literal>
<br />
Ship to:
<asp:Literal ID="lShipTo" runat="server"></asp:Literal>
<br />
<asp:Button ID="btnConfirm" runat="server" Text="Confirm" OnClick="Finish" />
</asp:PlaceHolder>
PlaceOrder.aspx.cs
1: public partial class PlaceOrder : Page {
2: private IDaoFactory daoFactory = new NHibernateDaoFactory();
3: private BurrowFramework bf = new BurrowFramework();
4:
5: /// <summary>
6: /// Store the placing order in the Conversation.Items so that it has the same life span as the Conversation
7: /// </summary>
8: public Order placingOrder {
9: get { return (Order) bf.CurrentConversation.Items["placingOrder"]; }
10: set { bf.CurrentConversation.Items["placingOrder"] = value; }
11: }
12:
13: protected void Page_Load(object sender, EventArgs e) {
14: if (!IsPostBack) {
15: ddlCustomers.DataSource = daoFactory.GetCustomerDao().GetAll();
16: ddlCustomers.DataBind();
17: }
18: }
19:
20: protected void Step1(object sender, EventArgs e) {
21: //start an atomic Conversation that span over postbacks
22: bf.CurrentConversation.SpanWithPostBacks(TransactionStrategy.BusinessTransaction);
23: Customer selectedCustomer = daoFactory.GetCustomerDao().GetById(ddlCustomers.SelectedValue, false);
24: placingOrder = new Order(selectedCustomer) ;
25: phSelectCustomer.Visible = false;
26: phEnterShipToName.Visible = true;
27: }
28:
29: protected void btnStep2(object sender, EventArgs e) {
30: placingOrder.ShipToName = tbShipToName.Text;
31: phEnterShipToName.Visible = false;
32: phConfirm.Visible = true;
33: lCustomer.Text = placingOrder.OrderedBy.ToString();
34: lShipTo.Text = placingOrder.ShipToName;
35: }
36:
37: protected void Cancel(object sender, EventArgs e) {
38: bf.CurrentConversation.GiveUp(); // give up the current spanning conversation
39: phEnterShipToName.Visible = false;
40: StartOver();
41: }
42:
43: private void StartOver() {
44: phSelectCustomer.Visible = true;
45: }
46:
47: protected void Finish(object sender, EventArgs e) {
48: placingOrder.OrderDate = DateTime.Now;
49: daoFactory.GetOrderDao().Save(placingOrder);
50: bf.CurrentConversation.FinishSpan(); //finish up the current spanning conversation
51: placingOrder = null; //reset the placing order to null after conversation is done is a good practice
52: phConfirm.Visible = false;
53: StartOver();
54: }
55: }
In step 1, "bf.CurrentConversation.SpanWithPostBacks();" is the line to let the system know that the current conversation needs to span after the end of the current request. Without this line, the current conversation will closed at the end of the request (as in OpenSessionPerView mode). Then the code created an order entity and store it into the bf.CurrentConversation.Items["placingOrder"]. IConversation.Items is a data container through which you can store the data into the current conversation so that the data will have the same life cycle as the conversation. Step2 simple modifies the shipToName property of the placingOrder. The finish step finally persists the order and calls bf.CurrentConversation.FinishSpan(); then Burrow will commit and close this multi-request conversation at the end of this request.
For more information about NHibernate.Burrow, here is a guide to the pages of this wiki site:
Get Started - tells how to setup the Burrow framework.
Burrow Conversation Explained - gives more detail in long conversation.
StatefulField Attributes - explains how Burrow.WebUtil attributes can simplify states maintenance for ASP.NET controls and pages
FAQ - you know what it is.