Custom Products and Products Entries using Metadata Plus configured by an XML file Part I – EPiServer Commerce

Custom Products and Products Entries using Metadata Plus configured by an XML file Part I – EPiServer Commerce

EPiServer Metadata Plus allows you to customize the Catalog and Order systems custom in several ways. One way is by creating classes that inherit from ProductContent for Products and VariationContent for Product SKUs and then set attributes to the classes properties in order to configure them as you want.

CustomProductEntry

[CatalogContentType(GUID = "8d664789-3e96-409e-b418-baf807241f7c",
MetaClassName = "CustomProductEntry", DisplayName = "Custom Product Entry")]
public class CustomProductEntry : ProductContent
{
[IncludeValuesInSearchResults]
[DecimalSettings(18, 2)]
public virtual decimal Tax{ get; set; }

[DecimalSettings(18, 2)]
public virtual decimal Percentage{ get; set; }

[CultureSpecific]
public virtual string AnotherId{ get; set; }

public virtual string Description { get; set; }
}

CustomProductSKU

[CatalogContentType(MetaClassName = "CustomProductSKU", DisplayName = "Custom Product SKU", Description = "")]
public class CustomProductSKU : VariationContent
{
[Display(Name = "Case (short string)", Description = "")]
public virtual string Case { get; set; }

[Display(Name = "Size (short string)", Description = "")]
public virtual string Size{ get; set; }

[Display(Name = "Retail (short string)", Description = "")]
public virtual string Retail { get; set; }

public virtual string Description { get; set; }
}
}

However, it does have some limitations such as: there is no way to specify if a string property is a short or long string, cannot define image types, among others. Thus, we decided to generate this classes using another API inside EpiServer commerce which overcome some of these issues. For instance, Mediachase.MetaDataPlus.Configurator.MetaClass.Create method. The only problem with this API is that requires a lot of code to achieve almost the same results as the inherited class, so I decided to create an XML file which will serve as a configuration file with all the needed properties for the new API.

This post will explain the XML file and the parser, the second post will go through the use of the parsed XML object with the Mediachase.MetaDataPlus.Configurator API, so the steps for the first part are the following:

  1. Create the XML file
  2. Create the model classes for XML parsing

So once again without more hustle lets code !!!

Create the XML file

First, we will create an XML file which will create the same structure as the classes above.

<?xml version="1.0"?>
<Meta>
<MetaClasses>
<MetaClass ParentClassName="CatalogEntry" ClassName="CustomProductEntry" Description=""
FriendlyClassName="Custom Product Entry" TableClassName="CatalogEntryEx_CustomProductEntry" >
</MetaClass>
<MetaClass ParentClassName="CatalogEntry" ClassName="CustomProductSKU" Description=""
FriendlyClassName="Custom Product SKU" TableClassName="CatalogEntryEx_CustomProductSKU" >
</MetaClass>
</MetaClasses>
<MetaFields>
<MetaField FieldName="Tax" FriendlyFieldName="Tax (decimal)" FieldType="Decimal"
Length="0" AllowNulls="true" CultureSpecific="false" AllowSearch="true" Namespace="Mediachase.Commerce.Catalog">
<ExtraAttribute AttributeName="MdpPrecision" AttributeValue="18"/>
<ExtraAttribute AttributeName="MdpScale" AttributeValue="2"/>
<Joins>
<Join To="CustomProductEntry"/>
</Joins>
</MetaField>
<MetaField FieldName="Percentage" FriendlyFieldName="Percentage (decimal)" FieldType="Decimal"
Length="0" AllowNulls="true" CultureSpecific="false" AllowSearch="true" Namespace="Mediachase.Commerce.Catalog">
<ExtraAttribute AttributeName="MdpPrecision" AttributeValue="18"/>
<ExtraAttribute AttributeName="MdpScale" AttributeValue="2"/>
<Joins>
<Join To="CustomProductEntry"/>
</Joins>
</MetaField>
<MetaField FieldName="AnotherId" FriendlyFieldName="Another Id (short string)" FieldType="ShortString"
Length="0" AllowNulls="true" CultureSpecific="true" AllowSearch="true" Namespace="Mediachase.Commerce.Catalog">
<Joins>
<Join To="CustomProductEntry"/>
</Joins>
</MetaField>
<MetaField FieldName="Description" FriendlyFieldName="Description (short string)" FieldType="LongString"
Length="0" AllowNulls="true" CultureSpecific="false" AllowSearch="true" Namespace="Mediachase.Commerce.Catalog">
<Joins>
<Join To="CustomProductEntry"/>
<Join To="CustomProductSKU"/>
</Joins>
</MetaField>
<MetaField FieldName="Case" FriendlyFieldName="Case (short string)" FieldType="ShortString"
Length="0" AllowNulls="true" CultureSpecific="false" AllowSearch="true" Namespace="Mediachase.Commerce.Catalog">
<Joins>
<Join To="CustomProductSKU"/>
</Joins>
</MetaField>
<MetaField FieldName="Size" FriendlyFieldName="Size (short string)" FieldType="ShortString"
Length="0" AllowNulls="true" CultureSpecific="false" AllowSearch="true" Namespace="Mediachase.Commerce.Catalog">
<Joins>
<Join To="CustomProductSKU"/>
</Joins>
</MetaField>
<MetaField FieldName="Retail" FriendlyFieldName="Retail (short string)" FieldType="ShortString"
Length="0" AllowNulls="true" CultureSpecific="false" AllowSearch="true" Namespace="Mediachase.Commerce.Catalog">
<Joins>
<Join To="CustomProductSKU"/>
</Joins>
</MetaField>
</MetaFields>
</Meta>

Metaclasses element contains the lists of meta classes to configure

  • MetaClass is a element which contains the class configuration
    • ParentClassName attribute sets the system is going to customize
    • ClassName attribute sets the name of the new class
    • Description attribute is self explanatory
    • FriendlyClassName attribute is the name that is going to be display in the commerce manager catalog part
    • TableClassName attribute sets the table name in the commerce database

MetaFields element contains the lists of meta fields to configure

  • MetaField is a element which contains the field configuration
    • FieldName attribute sets the field name
    • FriendlyFieldName attribute is the name that is going to be display in the commerce manager catalog part
    • FieldType attribute sets the type of the field (Decimal,ShortString,LongString,etc)
    • Length attribute is self explanatory, works for Strings, for numbers length is zero
    • AllowNulls attribute sets if the field allows null values
    • CultureSpecific attribute establish if the field support multiple languages
    • AllowSearch attribute defines if the field is included in the search index
    • Namespace attribute set the space where the field is going to be saved. For products it should be Mediachase.Commerce.Catalog

In addition, if you are going to set a decimal field you can set the extra elements MdpPrecision and MdpScale

<ExtraAttribute AttributeName="MdpPrecision" AttributeValue="18"/>
<ExtraAttribute AttributeName="MdpScale" AttributeValue="2"/>

Finally, the joins element will define to which of the classes this field belongs. So something like the XML below will assign the field to the CustomProductEntry and CustomProductSKU classes.

<Joins>
<Join To="CustomProductEntry"/>
<Join To="CustomProductSKU"/>
</Joins>

Create the model classes for XML parsing

Now, we will create several classes in order to be able to parse the xml into an object. The classes to create are: Meta, MetaClasses, MetaClass, MetaFields, MetaField, Joins, Join and ExtraAttribute. Lets begin!

Meta

using System.Xml.Serialization;

///
<summary>
    /// The meta data class is the core element of the
/// xml file.
/// </summary>

    [XmlRoot]
public class Meta
{
///
<summary>
        /// Gets or sets the collection of meta classes.
/// </summary>

        [XmlElement]
public MetaClasses MetaClasses { get; set; }

///
<summary>
        /// Gets or sets the collection of meta fields.
/// </summary>

        [XmlElement]
public MetaFields MetaFields { get; set; }
}
using System.Collections.Generic;
using System.Xml.Serialization;

///
<summary>
    /// The meta classes class contains all
/// the meta data which is going to be added
/// as a meta class in EpiServer commerce
/// </summary>

    [XmlRoot]
public class MetaClasses
{
///
<summary>
        /// Gets or sets the list of meta classes.
/// Each meta class represents an entity in
/// the system
/// </summary>

        [XmlElement]
public List MetaClass { get; set; }
}

MetaClasses

MetaClass

using System.Xml.Serialization;

///
<summary>
    /// The Meta Class element.
/// Part of the MetaClasses class
/// Contains all the information required
/// to create a meta class in EpiServer commerce
/// </summary>

    [XmlRoot]
public class MetaClass
{
///
<summary>
        /// Gets or sets the parent class name.
/// Which meta class inherits from
/// It depends of the type of meta class you want
/// to create
/// </summary>

        [XmlAttribute]
public string ParentClassName { get; set; }

///
<summary>
        /// Gets or sets the class name.
/// Non friendly name that the class is going to have
/// Hidden for editors
/// </summary>

        [XmlAttribute]
public string ClassName { get; set; }

///
<summary>
        /// Gets or sets the description.
/// It is an optional value
/// </summary>

        [XmlAttribute]
public string Description { get; set; }

///
<summary>
        /// Gets or sets the friendly class name.
/// Friendly name that the class is going to have
/// Always visible
/// </summary>

        [XmlAttribute]
public string FriendlyClassName { get; set; }

///
<summary>
        /// Gets or sets the table class name.
/// Name of the table where this meta class is going
/// to be saved. By default it uses the format
/// NameOfParentClass + Ex + _ + Name of the class
/// </summary>

        [XmlAttribute]
public string TableClassName { get; set; }
}

MetaFields

using System.Collections.Generic;
using System.Xml.Serialization;

///
<summary>
    /// The meta fields class contains all
/// the meta data which is going to be added
/// as a meta field in EpiServer commerce
/// </summary>

    [XmlRoot]
public class MetaFields
{
///
<summary>
        /// Gets or sets the list of meta classes.
/// Each meta field represents an entity in
/// the system
/// </summary>

        [XmlElement]
public List MetaField { get; set; }
}

MetaField

using System.Collections.Generic;
using System.Xml.Serialization;

///
<summary>
    /// The meta field class.
/// Part of the MetaClass class
/// Contains all the information required
/// to create a meta field in EpiServer commerce
/// </summary>

    [XmlRoot]
public class MetaField
{
///
<summary>
        /// Gets or sets the list extra attributes.
/// For instance, scale and precision of a decimal
/// or width and height of an image
/// </summary>

        [XmlElement]
public List ExtraAttribute { get; set; }

///
<summary>
        /// Gets or sets the join element.
/// It lists all the relationships of
/// the current meta field
/// </summary>

        [XmlElement]
public Joins Joins { get; set; }

///
<summary>
        /// Gets or sets the field name.
/// Non friendly name that the field is going to have
/// Non visible for editors
/// </summary>

        [XmlAttribute]
public string FieldName { get; set; }

///
<summary>
        /// Gets or sets the friendly field name.
/// Friendly name that the field is going to have
/// Always visible
/// </summary>

        [XmlAttribute]
public string FriendlyFieldName { get; set; }

///
<summary>
        /// Gets or sets the field type.
/// Type of the meta field taken from
/// the enumeration MetaDataType
/// </summary>

        [XmlAttribute]
public string FieldType { get; set; }

///
<summary>
        /// Gets or sets the length.
/// How many bytes is going to use this field
/// 0 By default means is going to use the
/// amount of bytes that the field type requires
/// </summary>

        [XmlAttribute]
public int Length { get; set; }

///
<summary>
        /// Gets or sets the allow nulls.
/// If the field accepts null values or not
/// </summary>

        [XmlAttribute]
public bool AllowNulls { get; set; }

///
<summary>
        /// Gets or sets the culture specific.
/// If the field has a different version for every
/// language available in EpiServer commerce
/// </summary>

        [XmlAttribute]
public bool CultureSpecific { get; set; }

///
<summary>
        /// Gets or sets the allow search.
/// If the field can be searched
/// </summary>

        [XmlAttribute]
public bool AllowSearch { get; set; }

///
<summary>
        /// Gets or sets the namespace which will be used to
/// identify if is a private or public meta field.
/// </summary>

        [XmlAttribute]
public string Namespace { get; set; }
}

Joins

using System.Collections.Generic;
using System.Xml.Serialization;

///
<summary>
    /// The Joins class contains all the
/// relations of the meta field
/// </summary>

    [XmlRoot]
public class Joins
{
///
<summary>
        /// Gets or sets the list of joins
/// for this meta field
/// </summary>

        [XmlElement]
public List Join { get; set; }
}

Join

using System.Xml.Serialization;

///
<summary>
    /// The Join element.
/// Is an optional part of a meta field
/// which specifies which meta classes this
/// field is related to
/// </summary>

    [XmlRoot]
public class Join
{
///
<summary>
        /// Gets or sets to which meta class
/// the meta field is related
/// </summary>

        [XmlAttribute]
public string To { get; set; }
}

ExtraAttribute

using System.Xml.Serialization;

///
<summary>
    /// The extra attribute element.
/// Is an optional part of a MetaField class
/// Contains additional information about a meta field
/// </summary>

    [XmlRoot]
public class ExtraAttribute
{
///
<summary>
        /// Gets or sets the attribute name.
/// Must be one of the attributes inside the
/// MetaFieldAttributeConstants
/// </summary>

        [XmlAttribute]
public string AttributeName { get; set; }

///
<summary>
        /// Gets or sets the attribute value.
/// It can be a boolean, int as a string value
/// depending of the attribute name
/// </summary>

        [XmlAttribute]
public string AttributeValue { get; set; }
}

With those classes we cover all we need to parse the XML file to an object. In the next part we will use this object with the Commerce API to create the meta classes and fields required.

I hope it will help someone and as always keep learning !!!

Written by:

Jorge Cardenas

Developer with several years of experience who is passionate about technology and how to solve problems through it.

View All Posts

Leave a Reply