Virtual Happy Hour this month, Jun 28, we'll be getting a sneak preview at our soon to launch SaaS CMS!

Try our conversational search powered by Generative AI!

Giuliano Dore
Feb 20, 2021
(7 votes)

How to migrate from uCommerce to EPiServer Commerce part 0: skus and tree structure


For one of our projects, we have the opportunity to migrate data from a uCommerce database / project into EPiServer Commerce. Migrating from one e-commerce platform to another is often a bumpy ride; the conventions are not the same, the data might not fit within the new data structures, and testing can take quite some time. I am hoping that this article will help developers who are facing the same challenges that we did.

Disclaimer: the versions of the tools / frameworks discussed in this article are listed below:

  • uCommerce for Sitecore
  • Sitecore 8.2
  • EPiServer CMS 11.20
  • EPiServer Commerce 13.25

Disclaimer: the statements in this article are based on the version that we used for our migration; some definitions might have been updated in the latest versions of the e-commerce platforms listed above. Feel free to drop a comment if you are doing a migration and you want to share some nuggets of wisdom 😊

Now that we have the basics covered, let’s get started 😊.

Products and variants skus: it’s different

In uCommerce, products and variants are stored in the same table called Product. In this table, a “product” (as row) can have a sku and a variant sku. What this structure means is that products and their variants share the same sku.

The following structure is possible in UCommerce:

  • Product 1 – sku: product_1
    • Product 1 variant 1 – sku: product_1
    • Product 1 variant 2 – sku: product_1
  • Product 2 – sku: product_2

The main issue with this approach is that the sku property in UCommerce is not a unique identifier. How do we differentiate all the products and variant then? By using the variant sku property:

  • Product 1 – sku: product_1, variant sku: null (not a variant)
    • Product 1 variant 1 – sku: product_1, variant sku: var_1
    • Product 1 variant 2 – sku : product_1, variant sku: var_2

At this stage, we are thinking: that’s fine, I will use the variant sku as unique identifier. WRONG.

The variant sku is not a unique identifier either.

First, it can be null, which is never a good sign for a unique identifier. Second, the variant sku can be shared between variants as long as they don’t depend on the same product:

  • Product 1
    • Product 1 variant 1 – variant sku: var_1
  • Product 2
    • Product 2 variant 1 – variant sku: var_1

People familiar with the EPiServer Commerce conventions already know that it’s not possible to share a sku between products and variants. It must be unique and non-nullable regardless of your parent product.

So, what’s a unique identifier in uCommerce? It’s the combination between a sku and their variant sku.

As a side note, this ‘a unique code is actually a combination of 2 properties’ is not really my cup of tea. It sounds like a very challenging technical design to maintain.

So, what’s the move regarding the migration? I went with the following approach in EPiServer:

  1. We must extend products and variants using meta classes to allow 2 properties: legacy sku and legacy variant sku that will be mapped with the properties in that we discussed previously.
  2. When importing, we let EPiServer generate the ‘code’ property to avoid issues with duplicates.
  3. The content / commerce team can then look at the code property inside the EPiServer Commerce backoffice and update it accordingly.

While we are running the script, we might be thinking ‘why are we even importing those properties?’ the reason is very simple:

  1. During migrations, it’s a good practice to import as much data as you can, you can always decide to delete unnecessary data after successfully migrating your content.
  2. Tables / objects like orders in uCommerce are using the sku and variant sku as identifier for the content of the order items. Without those properties, it will be impossible to import order history.

Finally, it’s important to notice that products and variants in EPiServer offer a slightly different approach to data definition. EPiServer Commerce allows the ability to create distinct definitions for products and variants as meta classes. As the definitions are separated, it’s possible to have different properties between products and variants.

As an example:

  • A ‘CleaningProduct’ will have the following properties
    • Property1
    • Property2

And it will allow the following variant types:

  • ‘CleaningVariantXY’ and will have the following properties:
    • Property3
    • Property4
  • ‘CleaningVariantRT’ will have the following properties:
    • Property5
    • Property6

In this example, the definition for CleaningVariantXY doesn’t have a definition for Property1 and Property2 by default.

In uCommerce, there’s no separate definition available for variants; variants are extensions of products; they share the same definitions.  If we want to “flag” a property as available at the variant level, we must define the property as ‘variant property’. Once this is done, we can create a variant for the product we selected and the property will be available and editable.

The catalogue tree structure

uCommerce has the following tree structure:

  • Stores (Called Product Catalog Groups in database)
    • Catalogues
      • Categories
        • Products
        • Categories
          • Products

EPiServer has the following tree structure:

  • Catalogues
    • Categories
      • Products
      • Categories
        • Products

We also know the following:

  • Every item template (stores, catalogues, categories, products) in the uCommerce tree can have a custom definition where we can set custom properties for the template to fit our needs. In EPiServer Commerce we can add definitions by using MetaClasses to extend the list of properties that a commerce object can contain.
  • EPiServer Catalogues (CatalogContent) do not inherit from IMetaData; I couldn’t find a way online to use MetaClasses with catalogues so I assumed it was not possible. Luckily for me, the stores that I was pulling from the database didn’t contain any custom properties.
  • It is possible to have as many layers of categories as needed. Categories can have sub-categories and sub-sub-categories without any limit to the number of levels we need.

If there is information inside your uCommerce stores objects that you need to keep, the following options are available:

  • Use a separate persistent entity to match the properties that are needed like a DataStore or custom tables with a direct relation to the CatalogContent
  • Move the properties one level down as categories in EPiServer can use MetaClasses to extend the list of properties.

I believe the goal of a migration is to migrate data while offering a smooth transition; it involves keeping as much data as possible in a familiar structure. My goal here was not to challenge the previous data structure but making sure all the data was successfully migrated. It is absolutely possible to challenge a data structure but I would recommend to migrate first and then offer to refactor once the migration is complete.  

Once we have an understanding of our legacy commerce tree and we have the definitions for the new tree, we can start to programmatically add items in our commerce tree. In my scenario, this is the before/after:


  • Store1
    • Catalog1
      • Category1
        • Product1
        • Product2
      • Category2
        • Product3
        • Product4
      • Catalog2
        • Category3
          • Product5
        • Store2
          • Catalog3
            • Category4
              • Product6



  • Catalog1 (previously identified as Store1)
    • Category1 (previously identified as Catalog1)
      • SubCategory1 (previously identified as Category1)
        • Product1
        • Product2
      • SubCategory2 (previously identified as Category2)
        • Product3
        • Product4
      • Category2 (previously identified as Catalog2)
        • SubCategory3 (previously identified as Category3)
          • Product5
        • Catalog2 (previously identified as Store2)
          • Category3 (previously identified as Catalog3)
            • SubCategory4 (previously identified as Category4)
              • Product6

A note about migration strategies

Migration scripts can be tricky depending on the volume of data to transfer. For some lucky scenarios, you might be able to have a single script that imports everything in one go, happy days.

Unfortunately, most enterprise databases are pretty heavy and sometimes your only option will be to split your migration into multiple scripts.  

 We could have the following scenarios:

  • A script for catalogs and categories, a script for products, a script for product “custom” properties or
  • A script for catalogs, a script for categories, a script for products and their properties or
  • A script for every single list of data to transfer, catalogs, categories, sub categories, products, product properties, category properties, etc.

If you have to run multiple scripts one after another – for example adding products to categories that we just created – your best bet is to save the previous database items ids inside your new EPiServer Commerce definitions.


  • Are you looking for the parent category of your product? Define a ‘legacy category id’ property in your category meta class so you can find refer to that property when saving a product.
  • Are you looking to update a product with properties from the legacy database? A ‘Legacy product id’ is your best bet to find the product.

That’s it for today 😊

For the next articles, I will be looking to write about the functions to use to save data inside the Commerce database, pricing and how to export data from a database at a code level.

I hope it was helpful. If you are familiar with migrations please reach out, I would love to hear your feedback about this article.

Many thanks to the EPiServer Community and especially Praful, Quan and Sanjay for their help.

Feb 20, 2021


Sanjay Kumar
Sanjay Kumar Feb 21, 2021 04:44 AM

Great job Giuliano!

Giuliano Dore
Giuliano Dore Feb 21, 2021 01:21 PM

Thanks Sanjay 😊 

Adam B
Adam B Feb 22, 2021 10:58 AM

This is really helpful Giuliano!

Giuliano Dore
Giuliano Dore Feb 22, 2021 04:03 PM

Thanks Adam 😊

Please login to comment.
Latest blogs
Enhancing online shopping through Optimizely's personalized product recommendations

In this blog, I have summarized my experience of using and learning product recommendation feature of Optimizely Personalization Artificial...

Hetaxi | Jun 18, 2024

New Series: Building a .NET Core headless site on Optimizely Graph and SaaS CMS

Welcome to this new multi-post series where you can follow along as I indulge in yet another crazy experiment: Can we make our beloved Alloy site r...

Allan Thraen | Jun 14, 2024 | Syndicated blog

Inspect In Index is finally back

EPiCode.InspectInIndex was released 9 years ago . The Search and Navigation addon is now finally upgraded to support Optimizely CMS 12....

Haakon Peder Haugsten | Jun 14, 2024

Change the IP HTTP Header used for geo-lookup in Application Insights


Johan Kronberg | Jun 10, 2024 | Syndicated blog