allBlogsList

Sitecore Commerce Cart with MongoDB session

The following will show how to create a Commerce Cart that is persisted to the MongoDB session. MongoDB will give the ability to support a dynamic cart with high availability.

The first step to setting up the cart is to add a reference MongoDB.Bson and MongoDB.Drive. This reference will allow us to update the MongoDB and query the results.

After the references have been added the next step is to create a CartController. The CartController will have two methods. The first method will support the ability to add a product to the customers cart. The second method will allow us to retrieve the list of products in the users cart.

The Add to cart method will take two parameters, the product sku and the quantity. If the quantity is not specified the value will default to 1.

[HttpGet]
public ActionResult AddToCart(string sku, uint quantity = 1)
{
}

To add the product from the cart we will need to grab the user cart. To grab the user cart with Sitecore Commerce Connect we will use CartServiceProvider. The CartServiceProvider has the LoadCart function. This function will return the Commerce Connect cart.

CartServiceProvider provider = new CartServiceProvider();
Cart cart = provider.LoadCart(new LoadCartRequest(Sitecore.Context.Site.Name, "current")).Cart;

After the cart has been pulled the next step is create a new cart line. After the cart line has been added the cart user id is updated to the current user, then the cart line will add to the cart.

AddCartLinesRequest request = new AddCartLinesRequest(cart, lines);

request.Cart.UserId = Tracker.Current.Contact.ContactId.ToString();
cart = provider.AddCartLines(request).Cart;

The last part of the AddToCart is returning the cart to the user.

return new JsonResult { Data = cart, JsonRequestBehavior = JsonRequestBehavior.AllowGet };

To support the user viewing the cart we are going to create a function that that will grab the cart using Commerce Connect.

[HttpGet]
public ActionResult GetCart()
{
    CartServiceProvider provider = new CartServiceProvider();

    Cart cart = provider.LoadCart(new LoadCartRequest(Sitecore.Context.Site.Name, "current")).Cart;

    return new JsonResult { Data = cart, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}

The next step will be to update Sitecore Commerce to support storing and retrieving the cart from MongoDB. Adding support for MongoDB will be done in 3 parts: Updating Sitecore config to use MongoDB as the session state, add ability to query the cart in MongoDB and add ability to save the cart to the MongoDB.

Updating the Sitecore config to reference the session state will be specificed under the App_Config/Include. The below example instructs Sitecore to use LoadCartFromMongoStore for commerce.carts.loadCart and commerce.carts.saveCart to use SaveCartToMongoStore.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
	<sitecore>
    <pipelines>
      <commerce.carts.loadCart>
        <processor type="CommerceAnalyticsModule.Pipelines.LoadCartFromMongoStore, CommerceAnalyticsModule"
								 patch:before="processor[@type='Sitecore.Commerce.Pipelines.Carts.LoadCart.LoadCartFromEaState, Sitecore.Commerce']">
        </processor>
      </commerce.carts.loadCart>
      <commerce.carts.saveCart>
        <processor type="CommerceAnalyticsModule.Pipelines.SaveCartToMongoStore, CommerceAnalyticsModule"
                 patch:before="processor[@type='Sitecore.Commerce.Pipelines.Carts.Common.SaveCartToEaState, Sitecore.Commerce']">
        </processor>
      </commerce.carts.saveCart>
    </pipelines>
	</sitecore>
</configuration>

Loading the cart from Mongo will be done in the LoadCartFromMongoStore.

using CommerceAnalyticsModule.BL;
using MongoDB.Bson;
using MongoDB.Driver.Builders;
using Sitecore.Commerce.Data.Carts;
using Sitecore.Commerce.Entities.Carts;
using Sitecore.Commerce.Pipelines;
using Sitecore.Commerce.Pipelines.Carts.Common;
using Sitecore.Commerce.Services.Carts;
using Sitecore.Diagnostics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace CommerceAnalyticsModule.Pipelines
{
    public class LoadCartFromMongoStore : CartPipelineProcessor
    {
        public LoadCartFromMongoStore()
        {
        }

        public override void Process(ServicePipelineArgs args)
        {
}
    }
}

The process function will query MongoDB to get the user cart. To query the MongoDB we will create the GetCurrentCart fuction. The function will return the BsonDocument.

public BsonDocument GetCurrentCart()
{
}

Each cart that is generated will be assigned a GUID. The cart id will be stored in the users cookie. The cart id will be used to reference a specific cart. If the cookie does not exist or is empty then the function will return null.

var cookie = HttpContext.Current.Request.Cookies["cart"];

if (cookie == null || String.IsNullOrWhiteSpace(cookie.Value))
    return null;

string cartId = cookie.Value;

After the cart id has been pulled, we query MongoDB for the cart with the cart id then the results are returned.

var collection = CartHelper.GetCartCollection();

var cartDoc = collection.FindOne(Query.EQ("CartId", cartId));

return cartDoc;

Sitecore Commerce Connect calls the Process function to populate the populated. The Process function will use the GetCurrentCart() to lookup the user cart, then it will create a cart and assign it to the result.

public override void Process(ServicePipelineArgs args)
{
    BsonDocument cartDoc = GetCurrentCart();

    Cart cart = new Cart();
    cart.ShopName = "website";

    CartResult result = (CartResult)args.Result;
    result.Cart = cart;
}

After the basic cart has been created, the document will be lookup in MongoDB. If the document was found, we save the CartId and CustomerId to the cart.

if (cartDoc != null)
{
    cart.ExternalId = cartDoc["CartId"].AsString;
    cart.CustomerId = cartDoc["CustomerId"].AsString;
}

Next, we check to see if any order line exists. The order lines will be added to the cart with the product sku and quantity being populated.

BsonArray orderLines = cartDoc["OrderLine"] as BsonArray;

List

We now have the ability to query the MongoDB. The next step is to save the cart to the MongoDB. To save the cart we are going to create a class named SaveCartToMongoStore that inherits from CartPipelineProcess.

using CommerceAnalyticsModule.BL;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using Sitecore.Commerce.Entities.Carts;
using Sitecore.Commerce.Pipelines;
using Sitecore.Commerce.Pipelines.Carts.Common;
using Sitecore.Commerce.Services.Carts;
using Sitecore.Diagnostics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace CommerceAnalyticsModule.Pipelines
{
    public class SaveCartToMongoStore : CartPipelineProcessor
    {
        public SaveCartToMongoStore()
        {

        }

        public override void Process(ServicePipelineArgs args)
        {
        }
    }
}

To save the cart we need to get the cart from the argument cart request.

public override void Process(ServicePipelineArgs args)
{
    CartRequestWithCart request = (CartRequestWithCart)args.Request;
    Cart cart = request.Cart;
}

After the cart has been created the cart lines will be processed. For each cart line the sku and quantity will be added to the MongoDB document.

BsonArray orderLines = new BsonArray();
foreach(CartLine line in cart.Lines)
{
    orderLines.Add(new BsonDocument { 
        {"sku", line.Product.ProductId}, 
        {"quantity", line.Quantity} });
}

After the order lines have been created the cart document will be created. The cart document will contain the customer id, cart id and order lines.

var document = new BsonDocument
{
    { "CustomerId", cart.CustomerId },
    { "CartId", cart.ExternalId},
    { "OrderLine", orderLines}
};

The cart external id is used to identify if the cart is new or already saved. If the external id is not populated the id will be generated and a new record will be inserted into the database. If the cart exists the MongoDB document will be updated.

MongoCollection<BsonDocument> collection = CartHelper.GetCartCollection();
if (isNewCart)
{
    collection.Insert(document);
}
else
{
    collection.Update((Query.EQ("CartId", cart.ExternalId)), Update.Replace(document));
}

After the data has been saved to the MongoDB. The cart id will be saved to the user cookie.

The last piece is to implement the CartHelper class. The class will be used to connect to the MongoDB and return the collection.

using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace CommerceAnalyticsModule.BL
{
    public class CartHelper
    {
        public static MongoCollection<BsonDocument> GetCartCollection()
        {
        }
    }
}

For this example we are going to connect to the local MongoDB server on port 27017

public static MongoCollection<BsonDocument> GetCartCollection()
{
    var client = new MongoClient("mongodb://localhost:27017");
}

Finally, connection has been established. The shopping cart database will be open and the cart collection will be returned. If the database does not exists or the collection does not exist they will automatically be created in MongoDB.

public static MongoCollection<BsonDocument> GetCartCollection()
{
    var client = new MongoClient("mongodb://localhost:27017");
    var database = client.GetServer().GetDatabase("ShoppingCart");
    var collection = database.GetCollection<BsonDocument>("Cart");
    return collection;
}

Below is the complete file for CartHelp.cs.

using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace CommerceAnalyticsModule.BL
{
    public class CartHelper
    {
        public static MongoCollection<BsonDocument> GetCartCollection()
        {
            var client = new MongoClient("mongodb://localhost:27017");
            var database = client.GetServer().GetDatabase("ShoppingCart");
            var collection = database.GetCollection<BsonDocument>("Cart");
            return collection;
        }
    }
}

SaveCartToMongoStore

using CommerceAnalyticsModule.BL;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using Sitecore.Commerce.Entities.Carts;
using Sitecore.Commerce.Pipelines;
using Sitecore.Commerce.Pipelines.Carts.Common;
using Sitecore.Commerce.Services.Carts;
using Sitecore.Diagnostics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace CommerceAnalyticsModule.Pipelines
{
    public class SaveCartToMongoStore : CartPipelineProcessor
    {
        public SaveCartToMongoStore()
        {

        }

        public override void Process(ServicePipelineArgs args)
        {
            CartRequestWithCart request = (CartRequestWithCart)args.Request;
            Cart cart = request.Cart;

            BsonArray orderLines = new BsonArray();
            foreach(CartLine line in cart.Lines)
            {
                orderLines.Add(new BsonDocument { 
                                {"sku", line.Product.ProductId}, 
                                {"quantity", line.Quantity} });
            }
            
            var document = new BsonDocument
            {
                { "CustomerId", cart.CustomerId },
                { "CartId", cart.ExternalId},
                { "OrderLine", orderLines}
            };

            bool isNewCart = false;
            if (cart.ExternalId == null)
            {
                cart.ExternalId = Guid.NewGuid().ToString();
                isNewCart = true;
            }

            MongoCollection<BsonDocument> collection = CartHelper.GetCartCollection();
            if (isNewCart)
            {
                collection.Insert(document);
            }
            else
            {
                collection.Update((Query.EQ("CartId", cart.ExternalId)), Update.Replace(document));
            }

            var cookie = new HttpCookie("cart", cart.ExternalId);
            HttpContext.Current.Response.AppendCookie(cookie);
        }
    }
}

LoadCartFromMongoStore.cs

using CommerceAnalyticsModule.BL;
using MongoDB.Bson;
using MongoDB.Driver.Builders;
using Sitecore.Commerce.Data.Carts;
using Sitecore.Commerce.Entities.Carts;
using Sitecore.Commerce.Pipelines;
using Sitecore.Commerce.Pipelines.Carts.Common;
using Sitecore.Commerce.Services.Carts;
using Sitecore.Diagnostics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace CommerceAnalyticsModule.Pipelines
{
    public class LoadCartFromMongoStore : CartPipelineProcessor
    {
        public LoadCartFromMongoStore()
        {
        }

        public override void Process(ServicePipelineArgs args)
        {
            BsonDocument cartDoc = GetCurrentCart();

            Cart cart = new Cart();
            cart.ShopName = "website";

            CartResult result = (CartResult)args.Result;
            result.Cart = cart;

            if (cartDoc != null)
            {
                cart.ExternalId = cartDoc["CartId"].AsString;
                cart.CustomerId = cartDoc["CustomerId"].AsString;

                BsonArray orderLines = cartDoc["OrderLine"] as BsonArray;

                List<CartLine> list = new List<CartLine>();

                foreach (BsonDocument lineDoc in orderLines)
                {
                    CartLine cartLine = new CartLine();
                    CartProduct cartProduct = new CartProduct();

                    cartProduct.ProductId = lineDoc["sku"].AsString;
                    cartLine.Quantity = Convert.ToUInt32(lineDoc["quantity"].AsInt64);
                    cartLine.Product = cartProduct;
                    list.Add(cartLine);
                }

                cart.Lines = list.AsReadOnly();
            }
        }

        public BsonDocument GetCurrentCart()
        {
            var cookie = HttpContext.Current.Request.Cookies["cart"];

            if (cookie == null || String.IsNullOrWhiteSpace(cookie.Value))
                return null;

            string cartId = cookie.Value;

            var collection = CartHelper.GetCartCollection();

            var cartDoc = collection.FindOne(Query.EQ("CartId", cartId));

            return cartDoc;
        }
    }
}

CartController.cs

using CommerceAnalyticsModule.BL;
using CommerceAnalyticsModule.Service;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using Sitecore.Analytics;
using Sitecore.Commerce;
using Sitecore.Commerce.Entities.Carts;
using Sitecore.Commerce.Services.Carts;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Web;
using System.Web.Helpers;
using System.Web.Mvc;

namespace CommerceAnalyticsModule.Controllers
{
    public class CartController : Controller
    {
        [HttpGet]
        public ActionResult GetCart()
        {
            CartServiceProvider provider = new CartServiceProvider();

            Cart cart = provider.LoadCart(new LoadCartRequest(Sitecore.Context.Site.Name, "current")).Cart;

            return new JsonResult { Data = cart, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
        }

        [HttpGet]
        public ActionResult AddToCart(string sku, uint quantity = 1)
        {
            CartServiceProvider provider = new CartServiceProvider();
            Cart cart = provider.LoadCart(new LoadCartRequest(Sitecore.Context.Site.Name, "current")).Cart;

            CartLine line = new CartLine
            {
                Quantity = quantity,
                Product = new CartProduct
                {
                    ProductId = sku
                }
            };

            Collection<CartLine> lines = new Collection<CartLine> {
                line
            };
            AddCartLinesRequest request = new AddCartLinesRequest(cart, lines);

            request.Cart.UserId = Tracker.Current.Contact.ContactId.ToString();
            cart = provider.AddCartLines(request).Cart;

            return new JsonResult { Data = cart, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
        }

    }
}

Website\App_config\Include\Main.config

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
	<sitecore>
    <pipelines>
      <commerce.carts.loadCart>
        <processor type="CommerceAnalyticsModule.Pipelines.LoadCartFromMongoStore, CommerceAnalyticsModule"
								 patch:before="processor[@type='Sitecore.Commerce.Pipelines.Carts.LoadCart.LoadCartFromEaState, Sitecore.Commerce']">
        </processor>
      </commerce.carts.loadCart>
      <commerce.carts.saveCart>
        <processor type="CommerceAnalyticsModule.Pipelines.SaveCartToMongoStore, CommerceAnalyticsModule"
                 patch:before="processor[@type='Sitecore.Commerce.Pipelines.Carts.Common.SaveCartToEaState, Sitecore.Commerce']">
        </processor>
      </commerce.carts.saveCart>
    </pipelines>
	</sitecore>
</configuration>

These above changes show how to support the ability to save a cart in Sitecore Commerce Connect using MongoDB. This will support a dynamic cart with high availability.