We all know that in the stateless ASP.NET framework, persisting page/control's states of entity type could be a cumbersome task - for example a customerEditor.ascx needs to remember which customer it's editing. One of the common solutions is to store the Id of the entity as a value of a hidden field or into the ViewState, then load the entity from the id when needed. The code will be something like
private Customer cust;
public Customer Cust {
get
{
if (ViewState["custID"] == null) return null;
if (cust == null)
cust = CustomerDAO.Instance.FindById((int)ViewState["custID"]);
return cust;
}
set {
cust = value;
if (value == null)
ViewState["custID"] = null;
else
ViewState["custID"] = value.Id;
}
}
This approach has a couple of shortcomings. First, as you can see here, it's cumbersome to do the null judgement and converting type for the Id object. If you have codes all over the application, it's duplicated code. Secondly and more importantly, it requires either ViewState or HiddenField, each has its own problem: ViewState could be disabled; HiddenField does not work well within Ajax.Asp.Net's UpdatePanel as only the HiddenFields in the updating UpdatePanel will reflect the change you made to its value.
Now Burrow offers a new approach - StatefulField attributes.
[StatefulField] attribute is the most basic one. Here is an example:
[StatefulField]
protected int count = 1;
You mark a field of your control/page with the attribute [StatefulField], the value will be persistent over postbacks. [StatefulField] can be used for fields of types that are serializable - preferably primitive fields. Normally for this kind of fields, you can store them in the ViewState. However [StatefulField] attribute make the code cleaner and more importantly it can work when ViewState is disabled ( sure, there is ControlState, but this is a lot easier than ControlState isn't it? )
[EntityField] and [EntityFieldDeletionSafe] are the enhanced versions of [StatefulField]. Both can only be used to persist entity fields in a control/page. Here is an example:
public partial class CustomerEditor : UserControl
{
[EntityFieldDeletionSafe]
protected Customer customerBeingEdited;
[EntityField]
protected User editingAdmin;
}
The CustomerEditor user control is written for administrator to edit a certain customer's profile. The above code uses the field customerBeingEdited to store the customer it's currently working on. The editingAdmin field is used to store the administrator user that is currently working with the system. Please note that [EntityField] and [EntityFieldNullSafe] should and can only be used on fields of entities that are already persisted. You cannot use them on transient entities. The major behavior difference between [EntityField] and [EntityFieldDeletionSafe] is that when you delete the entity without setting the field to null, [EntityField] will cause an Exception next time Burrow load entity value for it while [EntityFieldDeletionSafe] won't. Behind the scene, that's because that [EntityField] is using ISession.Load() to load the entity while [EntityFieldDeletionSafe] is using ISession.Get(). So the other interpretation here is that [EntityField] can return a proxy if your entity is set as LazyLoad=true, which could be a performance gain.
[ConversationalField] is a special version designed only for long Burrow Conversation. In long Burrow Conversation, you probably need to store some data that needs to have the same life span as the conversation. [ConversationField] attribute can be used for such fields. Here is an example:
public partial class Checkout: Page {
[ConversationalField]
protected Order placingOrder;
}
A checkout page that uses long Burrow Conversation stores the currently processing order in the placingOrder field. Marked with [ConversationalField] attribute, the value of this field will have the same life span as the conversation as its value is actually stored in the conversation instance. [ConversationalField] can be used on all types - not only entity but also non-entity types.
Important Notes: