Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Using Session State to Share Data between pages

1.00/5 (3 votes)
22 Jun 2011CPOL5 min read 22.9K   219  
How to use the Session collection, avoiding common pitfalls

Introduction

This article explores how to use the Session collection, avoiding common pitfalls.

Background

The Session object is a convenient way to store information to be accessed by several pages. Given the order in which Asp.net events are executed it is necessary to follow some guidelines to avoid headaches when implementing this tecnique.

The session object presents some advantages which make it ideal for storing complex data:

  • It is easy to save and retrieve complex data
  • No data does not travel between the browser and the server

In spite of these advantages , some care must be taken , because session objects remain in memory as long as the session remains active . If many large objects are created they can chew up the servers memory quite rapidly. To circumvent this problem the following can be done .
Use session objects only for small objects.

  • Reduce the session timeout to a reasonable time ( e.g 20 minutes )
  • Add a logout button which calls Session.Abandon() method to terminate the session. 
  • If many objects are going to be stored use the sessionState mode=SQLServer. This will reduce the performance, but will preserve the server’s memory. 

Using the Code

For the purpose of this article , we will use a very simple compound object : an Order which is composed of a header and a detail. 

C#
 public class OrderItem {
  private string _article;
  private Decimal _quantity;

  public String Article {
    get { return _article; }
    set { _article = value; }
  }
  public Decimal Quantity {
    get { return _quantity; }
    set { _quantity = value; }
  }
 
}

public class Order
{
  private DateTime _createdOn;
  private string _title;
  private List<OrderItem> _items;

  public DateTime CreatedOn {
    get { return _createdOn; }
  }
  public string Title {
    get { return _title; }
    set { _title = value; }
  }

  public List<OrderItem> Items {
    get { return _items; }
  }

  public Order(){
    _createdOn = DateTime.Now;
    _items = new List<OrderItem>();
  }
} 

Application Structure

We will use the Default page to edit the header ( title and CreatedOn properties ) and a Detail page for the details list ( article , quantity ) .

The key factor when using this technique is to have clear that the page_load event occurs before any of the control events. Hence , most of the Session handling will be done within this event.

The Root Page

The root page is the page where the topmost data of the object is edited. The form layout is very simple, it consists of two fields: title and createdOn. The createdOn field is read only. It also has two buttons : Clear, which clears the title and Details which navigates to the detail form.

RootPage.png

First we will define some helper methods to store and retrieve the Order object. These methods use the session object and a public order member.

C#
public Order order;

protected void setOrder() {
  Session["Object"] = order;
}

protected Order getOrder() {
  return (Order)Session["Object"];
}
We now need a way to know if the page is being loaded for the first time or as a result of coming back from the detail page. We will use another session object for this purpose:
C#
protected string getFromPage() {

  string result = (string)Session["FromPage"];
  if (result == null) {
    return "";
  } else {
    return result;
  }
}

Finally we need to handle the creation, storage and retrieval of the Order object. We have three cases:

The first case occurs when the application is starting and the Default page is being loaded for the first time. In this case we need to create the object and store it in the Session :

C#
if (!this.IsPostBack) {
  if (getFromPage() == "") {
  // Create the object and Save it in the Session
    order = new Order();
    setOrder();
  }       

Next , we need to handle the case in which we are returning from the detail page . In this case we only need to retrieve the object from the Session.

C#
else {
  order = getOrder();
}

Finally , we need to handle postbacks. When a page is re-loaded , the page is actually re-created using the viewstate information. Since our Order object is not part of the viewstate , it will be destroyed ( or more accurately the pointer will be set to null ), therefore we must retrieve it from the session state :

C#
} else {
    order = getOrder();
}

Now we need two more helper methods that allow us to move the data from the form into the object and vice versa. The methods are very simple because we only have two properties and one of the properties is read-only ( CreatedOn ).

C#
protected void fillForm() {
    this.txtTitle.Text = order.Title;
    this.txtCreatedOn.Text = order.CreatedOn.ToString();
  }
  
  protected void fillObject() {
    order.Title = txtTitle.Text;   
  }

The code for the button handlers is straightforward. The detail button simply redirects the user to the detail page. The clear button clears the title in the object and updates the form ( this could be done exactly in the other way (clear the control and update the object ).

C#
protected void btnNextPage_Click(object sender, EventArgs e) {
  fillObject();
  Response.Redirect("~/Detail.aspx");
}

protected void btnClear_Click(object sender, EventArgs e) {
  order.Title = "";
  fillForm();
}  <span class="Apple-style-span" style="color: rgb(255, 153, 0); font-family: Verdana, Arial, sans-serif; font-size: 13px; font-weight: bold; line-height: 16px; white-space: normal; "> </span>

The Detail Page 

The details form is made of a gridView in which we can view the order items and a couple of fields to add new details to the Order and two buttons: one to add the items and another to navigate back to the root page ( default.aspx) 

DetailPage.png

In the detail form we need two helper methods: one to get the order object and another to set the source page :

C#
protected void getOrder() {
  order = (Order)Session["Object"];
}

protected void setPage(){
  Session["FromPage"] = Page.Form.ID;
}

Since this is a detail screen , the code for the page_load event is a lot simpler. We always read the Order object when the event is executed ( because when this screen is reached , the root page has already created the Order object).

C#
protected void Page_Load(object sender, EventArgs e){
  //The object is allways read from the Session
  getOrder();
  if (this.IsPostBack == false) {
    gvItems.DataSource = order.Items;
    gvItems.DataBind();
  }
}

Now we add the code for the “Add” button : We create an order item object , we fill it with the data from the form , we add it to the Items list in the order object and we update the GridView control.

C#
protected void btnAdd_Click(object sender, EventArgs e) {
  Decimal qty;

  OrderItem item = new OrderItem();
  item.Article = txtArticle.Text;
  Decimal.TryParse(txtQuantity.Text,out qty);
  item.Quantity = qty;
  order.Items.Add(item);
  gvItems.DataSource = order.Items;
  gvItems.DataBind();

}

The last task is to create the handler for the “back” button. We need to call the setPage() method so that the root page (default.aspx) knows it is being loaded as a result of returning from the Detail page, we then redirect the application to the root page.

C#
protected void btnBack_Click(object sender, EventArgs e) {
   setPage();
   Response.Redirect("~/Default.aspx");
 }

Points of Interest

  Information can be passed between pages easily using the Session state. The order of events must be considered when using this technic to avoid getting a null object. The lifetime of the Session must be tunned if a large number of users is expected. This is a good technic to implement a shopping cart and small but complex objects. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)