EPiServer Commerce and using the CartHelper class

During development of an EPiServer Commerce 1R3 based ecommerce platform we encountered a strange problem.

If the end-user decided to leave the checkout flow and return, the taxes and shipping costs doubled, then tripled, then … you can see where this is going.
Of course, the client was not happy with my suggestion that it increases revenue so I looked into what was happening.

I noticed that the class we were using to wrap the CartHelper, CartService, was essentially creating a new CartHelper instance each time that it was accessed. A pretty obvious code smell.
To test that the issues we were seeing were caused by creating a new CartHelper each time it was used, I made the CartHelper property in CartService a static field so that it was initialised once.
Going through the process to reproduce the issue and it no longer occurred.
Now the problem was to find a way to make the CartService’s lifecycle session scoped, so that there was only a single instance of CartHelper available to the user during checkout.
[Decompiling the CartHelper to look at the underlying implementation I had discovered that it was using session based values, such as current user, so keeping it as a static field was not viable]

Luckily, EPiServer implements Dependency Injection using the popular Castle Windsor.

The following is a simplification of the wrapper service that ensures that there is only a single CartHelper instance per session. [We have a ton of other methods in there to manipulate the cart. note: if you have multiple carts, like for named wish lists, then this is a little too simplistic for you. But it is a good start.]

[ServiceConfiguration(typeof (CartService), Lifecycle = ServiceInstanceScope.HttpSession)]
public class CartService
{
	private readonly CartHelper cartHelper;

	public CartHelper CartHelper
	{
		get { 
			if(cartHelper == null)
			{
				cartHelper = new CartHelper(Cart.DefaultName);
			}
			return cartHelper; 

		}
	}

	public void Clear(){
		cartHelper.Delete();
		cartHelper = null;
	}
}

And it is used in your ActionResult method like so… [very over simplified]

[ShoppingCartValidator]
public partial class CheckoutController : BasePageController<CheckoutPage>
{
	private Injected<CartService> cartService;

	public ActionResult SomePage(CheckoutPage currentPage)
	{
		if (cartService.Service.CartHelper.IsEmpty)
		{
			return Redirect("/EmptyCartErrorPage ");
		}
		return Redirect("/SomeOtherPage");
	}
}

The important note is that the CartHelper instance is constructed lazily and that there is a method, CartService.Clear(), that removes the reference.
We must call CartServce.Clear() during the authentication of the user during login.
This is because the reference to the underlying cart helper instance is session scoped, and we want to remove the reference to the anonymous cart, and set it to the user specific cart.
You can put this in the Profile_MigrateAnonymous event in global.asax.cs, or if you are using Forms Authentication, just before you call SetAuthCookie.

4 thoughts on “EPiServer Commerce and using the CartHelper class

  1. Martijn van der Geest says:

    We had similar problems and also implemented a solution where 1 customer only has 1 CartHelper. However problems didn’t end there because sometimes we got SQL violation exceptions from the workflows because they were executed asynchronous sometimes, so i had to synchronize all public methods for cart/order operations. So i ended up with 1 big class for all cart/order operations just te be safe. Clean coding is hard here.

    Like

  2. Martin Graney says:

    I find it odd that for most session dependant functionality in EPiServer Commerce we have the CommerceContext.Current to access it.
    There should be, imho, a CommerceContext.Current.CartHelper that is always valid for the current request.

    Like

  3. vyan024 says:

    What about if you run the CartValidate workflow when user goes back? From what I understand, the CartValidate workflow will recalculate the total & subtotal without tax and shipping cost.

    p.s. I can’t reproduce your problem in R2 SP2

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s