Sitecore has five list field types that Glass Mapper maps: Checklist, Multilist, Multilist with Search, Treelist, and TreelistEx. Out of the box, Glass Mapper supports mapping these field types to two .NET collection types: IEnumerable<T> and IList<T>. In this post I'll cover how to use Glass Mapper to map your code models to these field types.

Example Templates

For this post I'll use two sample templates, Product and State, that look as follows:

  • Product
    • Name - Single-Line Text
    • EligibleStates - Checklist
  • State
    • Name - Single-Line Text
    • Abbreviation - Single-Line Text

The Product template has an EligibleStates field that holds a list of States. I chose to make the EligibleStates field a Checklist, but the Multilist, Multilist with Search, Treelist, or TreelistEx field types work too.

Interface Models

The code for the Product template looks like this:

[SitecoreType(TemplateId = "{00000000-0000-0000-0000-000000000001}", AutoMap = true)]
public interface IProduct  
{
    Guid Id { get; set; }
    string Name { get; set; }
    IList<IState> EligibleStates { get; set; }
}

Note that the EligibleStates property could also be of type IEnumerable<IState> and Glass Mapper would map it. Other .NET collection types, such as Array<T>, Collection<T>, List<T>, etc., are not supported by Glass Mapper out of the box and will not be populated if present on your model.

The code for the State template looks like this:

[SitecoreType(TemplateId = "{00000000-0000-0000-0000-000000000002}", AutoMap = true)]
public interface IState  
{
    Guid Id { get; set; }
    string Name { get; set; }
    string Abbreviation { get; set; }
}

Mapping from Sitecore in Controllers

To map list data from Sitecore in your controllers, just make the controller inherit from GlassController<T>, where T is the type of data source that your rendering uses (e.g., IProduct). You can then access the DataSource property to get your data from Sitecore. The EligibleStates property of the DataSource works as expected--all of the selected states in Sitecore are present.

public class ProductController : GlassController<IProduct>  
{
    public ActionResult Product()
    {
        var product = DataSource;
        var viewModel = new ProductViewModel
        {
            Name = product.Name,
            EligibleStates = product.EligibleStates.Select(s => $"{s.Name} ({s.Abbreviation})")
        };
        return View(viewModel);
    }
}

Mapping from Sitecore, Elsewhere

Grabbing list data from Sitecore outside of your controllers is no problem. Just pull the item you want out of a SitecoreContext or SitecoreService and iterate over all the items in your List<T> or IEnumerable<T> property.

using (var sitecoreContext = new SitecoreContext())  
{
    var product = sitecoreContext.GetItem<IProduct>("/sitecore/content/Products/Product1");
    var productName = product.Name;
    foreach (var state in product.EligibleStates)
    {
        var stateName = state.Name;
        var stateAbbr = state.Abbreviation;
    }
}

Modifying List Fields

To modify list fields, create a new instance of your item in Sitecore with the Create method on your SitecoreContext or SitecoreService, grab the items from Sitecore that you want to add to or remove from your list field, add or remove them, and save.

using (var sitecoreContext = new SitecoreContext())  
{
    var georgia = sitecoreContext.GetItem<IState>("/sitecore/content/Settings/States/Georgia");
    var productsFolder = sitecoreContext.GetItem<Item>("/sitecore/content/Settings/Products");
    var product = sitecoreContext.Create(productsFolder, "New Product");

    product.Name = "New Product";
    product.EligibleStates.Add(georgia);

    sitecoreContext.Save(product);
}

Concrete Models

The code for the Product template looks like this:

[SitecoreType(TemplateId = "{00000000-0000-0000-0000-000000000001}", AutoMap = true)]
public class Product  
{
    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<IState> EligibleStates { get; set; }
}

Again, the EligibleStates property could also be of type IList<IState>. Other .NET collection types, such as Array<T>, Collection<T>, List<T>, etc., are not supported by Glass Mapper out of the box and will not be populated if present on your model.

The code for the State template looks like this:

[SitecoreType(TemplateId = "{00000000-0000-0000-0000-000000000002}", AutoMap = true)]
public class State  
{
    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }
    public virtual string Abbreviation { get; set; }
}

Mapping from Sitecore

Mapping from Sitecore with concrete models is no different than mapping from Sitecore with interface models. See the examples above for how to map list data from Sitecore to concrete models.

Modifying List Fields

Populating list fields with concrete models is slightly different than with interface models. With concrete models we are able to instantiate our own objects as shown below; we don't have to rely on the SitecoreContext or SitecoreService to instantiate new objects for us. Instantiate your object, initialize the list property, populate it with items from Sitecore, and use the SitecoreContext or SitecoreService to create your object in Sitecore.

using (var sitecoreContext = new SitecoreContext())  
{
    var georgia = sitecoreContext.GetItem<State>("/sitecore/content/States/Georgia");
    var productsFolder = sitecoreContext.GetItem<Item>("/sitecore/content/Products");

    var product = new Product
    {
        Name = "New Concrete Product",
        EligibleStates = new List<State>()
    };
    product.EligibleStates.Add(georgia);

    sitecoreContext.Create(productsFolder, product);
}

One thing to note when working with concrete models is that you can only add items that already exist in Sitecore to lists; that is, you cannot instantiate a new item, add it to a list, and expect Glass Mapper to save it to Sitecore. This wasn't an issue with interface models, where we had to rely on the SitecoreContext or SitecoreService to first create our objects (in Sitecore) before using them.

Consider the following example where I try to create a new state, California, and add it to the eligible states for my new product:

using (var sitecoreContext = new SitecoreContext())  
{
    var california = new State
    {
        Name = "California",
        Abbreviation = "CA"
    };
    var productsFolder = sitecoreContext.GetItem<Item>("/sitecore/content/Products");

    var product = new Product
    {
        Name = "New Concrete Product",
        EligibleStates = new List<State>()
    };
    product.EligibleStates.Add(california);

    sitecoreContext.Create(productsFolder, product);
}

Glass Mapper will throw an exception on sitecoreContext.Create because it doesn't know what to do with the new state that was created, similar to this:

Could not find item to save value AbstractPropertyConfiguration Property: EligibleStates Type: Glass.Mapper.FakePropertyInfo

Although Glass Mapper doesn't support this out of the box, the brilliant Mike Skutta has implemented a task that you can plug into Glass Mapper to get this functionality.

Closing Thoughts

Glass Mapper does an excellent job of abstracting away the Sitecore API behind standard .NET classes. Do you make use of Glass Mapper for list fields in your projects? Let me know how you use it in the comments.

This post originally appeared on the Perficient Microsoft blog.