Wednesday, September 10, 2008

Shopping Cart


It may be practical to use the DataRow and DataTable objects to represent a shopping cart and it's items. However, I have found this approach to be counterproductive more often than not, especially in scenarios where the shopping cart doesn't correspond to a table in your database. In this article, we will create our own classes to represent the shopping cart and its items.

For simplicity, we will be ignoring any authentication and/or authorization requirements. But before we do so, I wanted to touch slightly on this topic while I got you thinking about it. A visitor's Shopping Cart can easily be stored into their Profile by using the .NET Profiles API. In doing so, you can store this information permanently for repeat and future visits. In the event a user logs in after they have added an item to their shopping cart, not to worry the Profiles API supports migrating anonymous profiles. Most can make a good argument on how this API cansuffer from poor performance and/or scalability, however with good state management practices, it gets a double thumbs up from me!

Code (ShoppingCartItem Class)

private int _intProductId;
private string _strProductName;
private int _intQuantity;
private decimal _decUnitPrice;

public ShoppingCartItem() : this(0, String.Empty, 0, 0m) { }

public ShoppingCartItem(int pProductId, string pProductName,
int pQuantity, decimal pUnitPrice)
{
this._intProductId = pProductId;
this._strProductName = pProductName;
this._intQuantity = pQuantity;
this._decUnitPrice = pUnitPrice;
}

public int ProductId
{
get { return this._intProductId; }
set { this._intProductId = value; }
}

public string ProductName
{
get { return this._strProductName; }
set { this._strProductName = value; }
}

public int Quantity
{
get { return this._intQuantity; }
set { this._intQuantity = value; }
}

public decimal UnitPrice
{
get { return this._decUnitPrice; }
set { this._decUnitPrice = value; }
}

public decimal ItemTotal
{
get { return this._intQuantity * this._decUnitPrice; }
}

public static ShoppingCartItem Empty
{
get { return new ShoppingCartItem(); }
}

public override bool Equals(object obj)
{
if(obj == null)
{
return false;
}
if(Object.ReferenceEquals(this,obj))
{
return true;
}
if(this.GetType() != obj.GetType())
{
return false;
}
ShoppingCartItem objCartItem = (ShoppingCartItem)obj;
if(this._intProductId == objCartItem._intProductId)
{
return true;
}
return false;
}

public override int GetHashCode()
{
return this._intProductId.GetHashCode();
}

Lets take a quick look at some methods that may not be so obvious. Specifically, the ones that were overridden. We redefined the Equals and GetHashCode methods to say two shopping cart items are the same if they have the same product id. This is done, so when we add or remove items from the shopping cart that are the same, we update the quantities instead of adding or removing the same product more than once.

Notice that the property ItemTotal does not derive from a private member variable. This property is helpful when binding your cart to a Web Control such as the GridView cause it allows you to show the total price for each item and can be formatted using the String Formatter "{0:c}".

Code (Shopping Cart Class)

public ShoppingCart() { }

public int Count
{
get
{
return this.List.Count;
}
}

public ShoppingCartItem this[int pIndex]
{
get
{
if (pIndex < 0 || pIndex > this.List.Count)
{
return ShoppingCartItem.Empty;
}
return (ShoppingCartItem)this.List[pIndex];
}
set
{
this.List[pIndex] = value;
}
}

public int Add(ShoppingCartItem pCartItem)
{
if (pCartItem.Quantity < 1)
{
return this.List.Count;
}
if(this.Contains(pCartItem))
{
ShoppingCartItem objDirtyCartItem =
this[this.IndexOf(pCartItem)];
objDirtyCartItem.Quantity =
objDirtyCartItem.Quantity +
pCartItem.Quantity;
this[this.IndexOf(pCartItem)] = objDirtyCartItem;
return this.List.Count;
}
return (this.List.Add(pCartItem));
}

public int IndexOf(ShoppingCartItem pCartItem)
{
return (this.List.IndexOf(pCartItem));
}

public void Remove(ShoppingCartItem pCartItem)
{
if (this.Contains(pCartItem))
{
ShoppingCartItem objDirtyCartItem =
this[this.IndexOf(pCartItem)];
objDirtyCartItem.Quantity =
objDirtyCartItem.Quantity -
pCartItem.Quantity;
if (objDirtyCartItem.Quantity > 0)
{
this[this.IndexOf(pCartItem)] = objDirtyCartItem;
}
else
{
List.Remove(pCartItem);
}
}
}

public bool Contains(ShoppingCartItem pCartItem)
{
return (List.Contains(pCartItem));
}

The ShoppingCart Class needs to inherit from CollectionBase. When I use the term "dirty" it means that the object has either been modified and/or needs to be updated. Notice I did not use the Insert method. Add more methods and properties as you feel necessary.

If you are not using the Profiles API as mentioned above, you can instead use state management to store the user's shopping cart information. This includes Session, Serialization, Appending to an authorization ticket, and so on... What ever you chose, make sure you evaluate your needs before you determine how you want to persist your cart information.

0 comments:

 

blogger templates | Make Money Online