Loading...
Area: Episerver Commerce
Applies to versions: 7.5

Upgrading Enoteca-based sites

Recommendations [hide]

Introduction

This document describes the procedure for upgrading sites that are based on the Enoteca sample templates, from version 1 R3 to 7.5 of Commerce. In EPiServer Commerce 7.5, some legacy functionality has been removed, which will force developers to update sites to use typed content. This document will go through the most important areas that need to be changed to be able to route to products.

Page provider and typed catalog content

In previous versions of EPiServer Commerce, a page provider was used for handling catalog content as pages. This page provider and its routes has been removed. This means that the page types used for catalog content will be obsolete. Therefore you will need to create new classes inheriting from the correct base class (for example VariationContent, ProductContent, NodeContent).

For each meta class, a corresponding typed content should be created, linking to the correct meta class. Each typed content item should have a typed template, inheriting from ContentPageBase<T>, or implementing IRenderTemplate<T>.

Upgrading steps in Deployment Center

If you did not already do this, open the EPiServer Deployment Center, select the site to upgrade and do the following:

  • Run the Upgrade site with SQL Server database option.
  • Run the Upgrade to EPiServer Commerce version 7.5.xxx.x (under Modules) option.
  • Run the Upgrade/disable Add-ons After Product Upgrade option.

Making the project compile

After upgrading the site in Deployment Center, you will need to go through the steps below to make the site compile properly.

Remove assemblies from project

Remove the EPiServer.Business.Commerce.UI assembly from the project. All "usings" referencing to EPiServer.Business.Commerce.UI must also be removed.

CatalogNodeBrowserProperty

The CatalogNodeBrowserProperty has been removed. Remove the BackingType attribute for properties using this property, and change the type for the property from "String" to "ContentReference". Add the UIHint attribute on the property, with value "catalognode" [UIHint("catalognode")].

ProductPickerProperty

The ProductPickerProperty has been removed. Remove the BackingType attribute for properties using this property, and change the type for the property from String to ContentReference. Add the UIHint attribute on the property, with value "catalogentry" [UIHint("catalogentry")].

BreadcrumbsFactory

The BreadcrumbsFactory has been removed, and the logic has to be reimplemented.

WineDetailTemplate

See below for details on how to implement this using typed content.

ProductHelper

In the ProductHelper class, you need to change the delegate for _getProductParentPageLink to Func<CatalogContentBase, string>. By doing this, some functions will get compilation errors that need to be fixed. Change the signature for the constructor that took "getProductParentPageLink" as parameter, to have the Func<CatalogContentBase, string> type.

GetProductParentPageLink

Change the implementation to use the "UrlResolver".

C#
var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
var currentContent = HttpContext.Current.Request.RequestContext.GetRoutedData<CatalogContentBase>();
return urlResolver.GetUrl(currentContent.ParentLink, currentContent.Language.Name);

GetRegionPageLink

This method needs to be reimplemented as follows.

C#
             if (string.IsNullOrEmpty(productCode))
            {
                return null;
            }            
var referenceConverter = ServiceLocator.Current.GetInstance<ReferenceConverter>();
            var contentLink = referenceConverter.GetContentLink(productCode);
            var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
return urlResolver.GetUrl(contentLink, languageCode);

BundleDetailTemplate

The ProductPageLink needs to be changed to contain following code:

C#
var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
    var currentContent = HttpContext.Current.Request.RequestContext.GetRoutedData<CatalogContentBase>();
    return urlResolver.GetUrl(currentContent.ParentLink, currentContent.Language.Name);

WineDetail

In Page_Load, the BreadcrumbFactory part can be removed.

C#
    protected void Page_Load(object sender, EventArgs e)
{
    LoadProductDetailControl();
    AddMetaContent();
    // Increase viewed count by calling the IncreaseView method in CmoGadgetController.
    if (!IsPostBack)
    {
        CmoGadgetController.IncreaseView(Code);
    }
}

RegionModule

The property CatalogFilterPath needs to be changed.

C#
var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
    var currentContent = HttpContext.Current.Request.RequestContext.GetRoutedData<CatalogContentBase>();
    return urlResolver.GetUrl(currentContent.ParentLink, currentContent.Language.Name);

BreadCrumbs

Change the logic related to PageData for the BreadCrumbs user control.

GetParentPageData

Change the method to handle "IContent" instead.

C#
private IContent GetParentPageData(IContent content)
{
    // Don't return a PageData object for root page.
    if (content == null || content.ParentLink == ContentReference.RootPage)
        return null;
    // if the page is a page in the page tree, get Page data for parent page
    var page = content as PageData;
    if (!PageReference.IsNullOrEmpty(content.ContentLink) && page != null)
    {
        var parentPage = GetPage(page.ParentLink);
        return (parentPage == null || !parentPage.VisibleInMenu) ? null : parentPage;
    }
    else if(!ContentReference.IsNullOrEmpty(content.ParentLink)) // get the parent page by the Catalog Node structure
    {
        var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
        return contentLoader.Get<CatalogContentBase>(content.ParentLink);
    }
    return null;
}

GetIgnoredPages

Remove the BreadcrumbsFactory code.

C#
    private List<PageReference> GetIgnoredPages()
{
    var list = new List<PageReference>();
    list.Add(PageReference.RootPage);
    list.Add(PageReference.WasteBasket);
    return list;
}

GenerateBreabCrumbs

Do as follows to make this method more generic.

C#
    private void GenerateBreabCrumbs()
{
    var currentContent = HttpContext.Current.Request.RequestContext.GetRoutedData<IContent>();

    if (currentContent == null)
        return;

    List<IContent> breadcrumbArr = new List<IContent>();

    while (currentContent != null)
    {
        var pageData = currentContent as PageData;
        if (pageData != null && IsVisible(pageData.PageLink) && !IsContainerPage(pageData))
        {
            breadcrumbArr.Add(currentContent);
        }

        // Get next visible parent
        currentContent = GetParentPageData(currentContent);
    }

    // re-arrange the list
    breadcrumbArr.Reverse();

    // binding data into the breadcrumbs
    EpiBreadCrumbs.DataSource = breadcrumbArr;
    EpiBreadCrumbs.DataBind();
}

ProductHelper

In the ProductHelper class, the "_getProductGuid" was changed to be of type Func<EntryContentBase, Guid>, and the constructor needs to be changed in the same way.

GetProductGuid

Change the method for getting product GUIDs.

C#
     /// 
    <summary>
/// Gets the product GUID.
/// 
    </summary>
/// 
    <param name="productCode">The product code.</param>
/// 
    <returns>
    The GUID of the page related the product code.</returns>
public virtual Guid GetProductGuid(string productCode)
{
    var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
    var referenceConverter = ServiceLocator.Current.GetInstance<ReferenceConverter>();

    var contentLink = referenceConverter.GetContentLink(productCode);
    var content = contentLoader.Get<EntryContentBase>(contentLink);
    return GetProductGuid(content);
}
/// 
    <summary>
/// Gets the product GUID.
/// 
    </summary>
/// 
    <param name="entryContent">The entry row.</param>
/// 
    <returns>
    The GUID of the product page related to the entry row.</returns>
public virtual Guid GetProductGuid(EntryContentBase entryContent)
{
    return entryContent.ContentGuid;
}

HistoryManager

In the constructor of the "HistoryManager", change the logic for splitting the string parameter.

C#
_values = values.Split(new[]{ ";" }, StringSplitOptions.RemoveEmptyEntries).ToList();

History

The History property then needs to be changed as follows.

C#
    public string History
{
    get { return string.Join(";", _values); }
}

PageEditing

PageEditing should be removed in all places where it exists.

DataInitialization

Change the module dependency as follows.

C#
[ModuleDependency(typeof(EPiServer.Commerce.Initialization.InitializationModule))]

Process

All routing-related code should be removed.

UseCurrentUICulture

Remove this where it is used.

DefaultSearchFilterHelper

Remove the current market parameter for the SearchEntries method in the SearchFilterHelper.

C#
return SearchFilterHelper.Current.SearchEntries(criteria, out count, responseGroup, cacheResults, cacheTimeout);

CmoGadgetController CmoGadgetController

Add a reference to EPiSercer.Commerce.UI to make this functionality work.

CheckoutController

The method CmoGadgetController.IncreaseOrder() needs to be changed.

C#
CmoGadgetController.IncreaseOrder(new[]{ lineItem });

Changing to typed content

DataInitialization

You will need to register the routes for the catalog data. The easiest way to do this is to add the following line to the Initialize method:

C#
CatalogRouteHelper.MapDefaultHierarchialRouter(RouteTable.Routes, false);

Creating new content types

WineSku

C#
        [CatalogContentType(MetaClassName = "WineSKU")]
public class WineSku : VariationContent
{
    public virtual decimal ABV { get; set; }
    public virtual string Closure { get; set; }
    public virtual string Color { get; set; }
    public virtual string Facet_Brand { get; set; }
    public virtual string Info_ModelNumber { get; set; }
    public virtual decimal Margin { get; set; }
    public virtual string Maturity { get; set; }
    public virtual bool Organic { get; set; }
    public virtual int RecommendBoost { get; set; }
    public virtual string Region { get; set; }
    public virtual double Size { get; set; }
    public virtual string Style { get; set; }
    public virtual string Taste { get; set; }
    public virtual string Varieties { get; set; }
    public virtual string Vintage { get; set; }
}

WineItemDisplayTemplate

C#
        [CatalogContentType(MetaClassName = "WineItemDisplayTemplate")]
public class WineItemDisplayTemplate : NodeContent
{
    public virtual string Info_Promotional_Message { get; set; }
}

WineStoreLandingNode

C#
        [CatalogContentType(MetaClassName = "WineStoreLandingNode")]
public class WineStoreLandingNode : NodeContent
{
    public virtual string Info_Promotional_Message { get; set; }
}

Changing the templates

The templates needs to be changed to be able to be called from the routing system. This can either be done by implementing IRenderTemplate<T>, or inheriting from ContentPageBase<T>. In this example we will do the later.

WineDetail

The WineDetail will inherit from ContentPageBase.

C#
public partial class WineDetail : ContentPageBase<WineSKU>{

Page_Load

In the Page_Load, we can remove the logic that gets the seoInfo and pageName, since we have those on CurrentContent.

AddMetaContent

We can remove the first line, that gets the seoItem, since we have that on CurrentContent. Set seoItem like this instead:

C#
var seoItem = CurrentContent.SeoInformation;

LoadProductDetailControl

Instead of getting this from the entry, change the way to get the templateUrl as shown below.

C#
var templateResolver = ServiceLocator.Current.GetInstance<TemplateResolver>();
        var template = templateResolver.Resolve(new HttpContextWrapper(HttpContext.Current), CurrentContent, TemplateTypeCategories.UserControl);
        var templateUrl = template.Path;

Code

Remove the Code property property. To get the code, use CurrentContent.Code instead.

CurrentCatalogEntry

Remove the CurrentCatalogEntry property.

WineDetailTemplate

In the WineDetailTemplate, you need to register a partial renderer for WineSku. This can be done by implementing the market interface IRenderTemplate<WineSku>.

C#
        public partial class WineDetailTemplate : BaseCatalogTemplate, IRenderTemplate<WineSku>
{

ProductPageLink

The ProductPageLink method needs to be reimplemented. The easiest way to get this URL is to use this URL is to use UrlResolver:

C#
    protected string ProductPageLink
{
    get
    {
        var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
        var currentContent = HttpContext.Current.Request.RequestContext.GetRoutedData<WineSku>();
        return urlResolver.GetUrl(currentContent.ParentLink, currentContent.Language.Name);
}
}

Changing Enoteca.Master

The master page also needs to be changed to work with catalog content instead of page data.

OnLoad

Instead of getting currentPage, use get currentContent like this:
C#
var currentContent = HttpContext.Current.GetRequestContext().GetRoutedData<IContent>();

Instead of using the LanguagePickerControl, the UrlResolver can be used to get the URL.

C#
var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
    var currentMarketService = ServiceLocator.Current.GetInstance<ICurrentMarket>();
    var language = currentMarketService.GetCurrentMarket().DefaultLanguage.Name;
    var url = urlResolver.GetUrl(currentContent.ContentLink, language);

When setting PageTitle.Text, currentContent.Name can be used instead of Property["PageName"].

C#
PageTitle.Text = string.Format(CultureInfo.InvariantCulture, _title, currentContent.Name, TitleSeparator, Configuration.Settings.Instance.SiteDisplayName);

See also

  • Upgrading section of the EPiServer Commerce SDK.
  • Breaking changes in the Upgrading section of the EPiServer Commerce SDK.
Do you find this information helpful? Please log in to provide feedback.

Last updated: Oct 20, 2016

Recommendations [hide]