Tuesday, April 14, 2009

New way to Store view state at Persistent Medium in .NET

Sometimes, it becomes necessity to store the view state at server side. It can be stored in session or at file IO. Most of us are aware of how to store the view state at server. If not aware of the traditional way of storing the view state at server then check it out.

I am going to explain another way, which I think is a great example of having object orientation.

.NET Provides us a class named PageStatePersister. Inheriting which we are going to create another class. I named it FilePageStatePersister.
The class will look like below.

public class FilePageStatePersister : PageStatePersister
{
#region Variables
/// <summary>
/// Holds the unique id for each of the view states.
/// </summary>
string _id = "";
/// <summary>
/// The folder path from where to read and write the view state files.
/// </summary>
string _viewStateFolderPath;
#endregion
#region Ctors
/// <summary>
/// Constructor for creating the object.
/// </summary>
/// <param name="page">reference of the page
/// <param name="id">unique id
public FilePageStatePersister(Page page, string id)
: base(page)
{
_id = id;
_viewStateFolderPath =page.Server.MapPath(Constants.VIEWSTATEPATH);

}
#endregion
#region Overrided Methods
/// <summary>
/// Load the view state from file system.
/// </summary>
public override void Load()
{
string viewStateData = GetViewState();
this.ViewState = this.StateFormatter.Deserialize(viewStateData);
}
/// <summary>
/// Save the view state to file system.
/// </summary>
public override void Save()
{
string viewStateData = this.StateFormatter.Serialize(this.ViewState);
StoreViewState(viewStateData);
}
#endregion
#region Class Methods
/// <summary>
/// Actually store the data to the file system.
/// </summary>
/// <param name="data">
private void StoreViewState(string data)
{
StreamWriter writer = null;
try
{
if (!Directory.Exists(_viewStateFolderPath))
{
Directory.CreateDirectory(_viewStateFolderPath);
}
writer = new StreamWriter(_viewStateFolderPath + _id);
writer.Write(data);
}
finally
{
writer.Close();
}
}
/// <summary>
/// Actually reads the data from the file system.
/// </summary>
/// <returns></returns>
private string GetViewState()
{
string data = "";

StreamReader reader = null;

try
{
reader = new StreamReader(_viewStateFolderPath + _id);
data = reader.ReadToEnd();
}
finally
{
reader.Close();
}

return data;
}
#endregion
}

We need to override Save and Load methods of the class.

Simple till now.

The class is ready to be used.
Now, the remaining portion is how to use it.
I have created a PageBase class, which inherits System.Web.UI.Page class. All the asp .net pages inherits from my PageBase class instead of inheriting from System.Web.UI.Page class.

We just need to override one of the properties available with System.Web.UI.Page.
The code will look like this.

/// <summary>
/// Get the Persiter object for storing view state.
/// If <see cref="DoSaveStateToPersistenceMedium"> is set to true then it will use
/// instance of <see cref="FilePageStatePersister"> class other wise it will use
/// default PageStatePersister.
/// </see>
protected override PageStatePersister PageStatePersister
{
get
{
if (DoSaveStateToPersistenceMedium)
{
if (_pageStatePersister == null)
{
string guid = "";

if (Request[Constants.VIEWSTATEKEY] == null)
{
guid = Guid.NewGuid().ToString();
}
else
{
guid = Request[Constants.VIEWSTATEKEY].ToString();
}

_pageStatePersister = new FilePageStatePersister(this, guid);

Literal literal = new Literal();
literal.Text = "<div><input type=\"hidden\" name=\"" + Constants.VIEWSTATEKEY + "\" value=\"" + guid + "\" /></div>";
this.Form.Controls.Add(literal);
}
}
else
{
_pageStatePersister = base.PageStatePersister;
}
return _pageStatePersister;
}
}


As I have developed a PageBase and some of the pages of the project may require the viewstate to be stored at server, I have added a property which will notify whether the viewstate will be stored at server side or it will be stored at client(which is default). This provides us the flexibility to change the behavior of the view state storing whenever required.

Thursday, December 25, 2008

Custom Business Objects binding to Hierarchical datasource controls

I am great believer of Object Oriented Concepts and its implementation. Recently, I was looking for a class which will provide me a way to bind my business objects to a hierarchical datasource. I was unable to find it and so finally decided to develop my own hierarchical datasource.
Here is the class which will allow us to bind any of the custom business object collection to .NET Treeview control or Menu control.
The prerequisites are
1) The collection should implement an interface. I named it IHierarchySupport. The interface definition is
public interface IHierarchySupport<T>
{
List
<T> GetChildren(string parentId);
T GetParent (string childId);
bool HasChildren(string parentId);
}


2) Here T is a custom business object. The only consideration in developing the business object should be to override ToString() method and return the id of the object. In my case my object definition looks like

public class SecurityObjectInfo
{
#region Variables
SecurityObjectInfo _parentObject;
#endregion
public int ObjectId
{
get;
internal set;
}
public SecurityObjectInfo ParentObject
{
get
{
return _parentObject;
}
}
public string ObjectName
{
get;
internal set;
}

public string PageName
{
get;
internal set;
}

public SecurityObjectInfo()
{
}

public override string ToString() { return ObjectId.ToString(); }
}

3) Now I need a collection which will hold my business objects. As I am using CSLA framework to develop my business objects my collection is inherited from ReadOnlyListBase. I have removed data access code for having simplicity. This collection will implement the IHierarchySupport interface. It will look like


public class SecurityObjects:ReadOnlyListBase<SecurityObjects,SecurityObjectInfo>,IHierarchySupport<SecurityObjectInfo>
{
private SecurityObjects()
{
}
public static SecurityObjects GetSecurityObjects(string userName)
{
return DataPortal.Fetch (new SingleCriteria(userName));
}

#region IHierarchySupport Members
public List GetChildren(string parentId)
{
List
<T> children;
if (parentId.Equals(string.Empty))
children =this.Where(childObject => childObject.ParentObject == null).ToList();
else
children = this.Where(childObject => childObject.ParentObject != null && childObject.ParentObject.ObjectId.ToString().Equals(parentId)).ToList();
return children;
}

public SecurityObjectInfo GetParent(string childId)

{

SecurityObjectInfo parent = null;
if (!childId.Equals(string.Empty))
parent = this.Where(childObject => childObject.ObjectId.ToString().Equals(childId)).Single();
return parent;
}


public bool HasChildren(string parentId)
{
List
<T> children = this.Where(childObject => childObject.ParentObject != null && childObject.ParentObject.ObjectId.ToString().Equals(parentId)).ToList();
return children.Count > 0 ? true : false;
}

#endregion
}

4) Now we are done with Business Objects part. I need a class which implements IHierarchicalDataSource and should be generic enough to accomodate any business objects collection which implements IHierarchySupport interface. So, my HierarchicalDataSource class looks like

public class HierarchicalDataSource
<T>: IHierarchicalDataSource
{
IHierarchySupport
<T> _collection ;


public HierarchicalDataSource(IHierarchySupport
<T> collection)
{
this._collection = collection;
}

public HierarchicalDataSourceView GetHierarchicalView(string viewPath)
{
return new DataSourceView(this, viewPath);
}

string GetChildrenViewPath(string viewPath, T obj)
{
return viewPath + "\\" + obj.ToString();
}

bool HasChildren(string objectId )
{
return _collection.HasChildren(objectId);
}

string GetParentViewPath(string viewPath)
{
return viewPath.Substring(0, viewPath.LastIndexOf("\\"));
}

#region classes that implement required interfaces
class DataSourceView : HierarchicalDataSourceView
{
HierarchicalDataSource
<T> _collection;
string _viewPath;

public DataSourceView(HierarchicalDataSource
<T> collection, string viewPath)
{
this._collection = collection;
this._viewPath = viewPath;
}

public override IHierarchicalEnumerable Select()
{
return new HierarchicalEnumerable(_collection, _viewPath);
}
}

class HierarchicalEnumerable : IHierarchicalEnumerable
{
HierarchicalDataSource
<T> _collection;
string _viewPath;

public HierarchicalEnumerable(HierarchicalDataSource
<T> collection, string viewPath)
{
this._collection = collection;
this._viewPath = viewPath;
}

public IHierarchyData GetHierarchyData(object enumeratedItem)
{
return new HierarchyData(_collection, _viewPath,(T) enumeratedItem);
}

public IEnumerator GetEnumerator()
{
string parentId = string.Empty;
if (_viewPath == "")
parentId = string.Empty;
else
parentId = _viewPath.Substring(_viewPath.LastIndexOf("\\") + 1);
return _collection._collection.GetChildren(parentId).GetEnumerator();
}
}

class HierarchyData : IHierarchyData
{
HierarchicalDataSource
<T> _collection;
T _object;
string _viewPath;

public HierarchyData(HierarchicalDataSource
<T> collection, string viewPath, T obj)
{
this._collection = collection;
this._viewPath = viewPath;
this._object = obj;
}

public IHierarchicalEnumerable GetChildren()
{
return new HierarchicalEnumerable(_collection, _collection.GetChildrenViewPath(_viewPath, _object));
}

public IHierarchyData GetParent()
{
return new HierarchyData(_collection, _collection.GetParentViewPath(_viewPath), _collection._collection.GetParent(_object.ToString()));
}

public bool HasChildren
{
get
{
return _collection.HasChildren(_object.ToString());
}
}

public object Item
{
get
{
return _object;
}
}

public string Path
{
get
{
return _viewPath;
}
}

public string Type
{
get
{
return _object.ToString();
}
}
}

We are done with development part :).
5) Now we need to implement our development and let me show you how easily can we do this. My code will bind my collection of security objects to menu control.

menu1.DataSource = new HierarchicalDataSource
<SecurityObjectInfo>(User.SecurityObjects);

System.Web.UI.WebControls.MenuItemBinding binding = new System.Web.UI.WebControls.MenuItemBinding();

binding.TextField = "ObjectName";

binding.ValueField = "ObjectId";

binding.NavigateUrlField = "PageName";

menu1.DataBindings.Add(binding);

menu1.DataBind();


Done............ Isn't it cool?