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 TextEligibleStates
State
Name
– Single-Line TextAbbreviation
– 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.