I make no secret of Glass Mapper being my current ORM of choice on the Sitecore platform but recently (not for the first time) I came across the need to control the mapping process in a crazier manner than Glass allows out of the box.
Glass provides a good set of structures to allow you to extend its behaviour when it comes to the mapping process and I utilised this to hook in and provide a ‘Delegate’ fluent mapping. This mapping allows you to delegate the behaviour of the mapping process back to code you control using a simple mappingContext.Delegate(x => x.MyField).GetValue(y => GetMeTheField(y.Item)). This is similar to the mappingContext.Field() or mappingContext.SitecoreInfo() methods that Glass Mapper provides out of the box.
Delegating control to your code.
In order to delegate control to code outside of Glass Mapper, I have created a SetValue and GetValue method on the property builder that handles the delegate mapping
using System; using System.Linq.Expressions; using Glass.Mapper.Sc; using Glass.Mapper.Sc.Configuration.Fluent; public class SitecoreDelegate : AbstractPropertyBuilder { public SitecoreDelegate(Expression<Func> ex) : base(ex) { } public SitecoreDelegate SetValue(Action mapAction) { this.Configuration.MapToCmsAction = mapAction; return this; } public SitecoreDelegate GetValue(Func mapFunction) { this.Configuration.MapToPropertyAction = mapFunction; return this; } }
The Delegate Mapper
In order for anything in Glass to be mapped successfully, you need to define a mapper object, this is done simply by inheriting from the AbstractDataMapper class or one of its derivatives. In this case we are inheriting to allow it handle the return from out delegated functionality.
using Glass.Mapper; using Glass.Mapper.Configuration; using Glass.Mapper.Sc; using Sitecore.Exceptions; public class SitecoreDelegateMapper : AbstractDataMapper { public override void MapToCms(AbstractDataMappingContext mappingContext) { SitecoreDelegatePropertyConfiguration config = this.Configuration as SitecoreDelegatePropertyConfiguration; SitecoreDataMappingContext context = mappingContext as SitecoreDataMappingContext; if (config == null) { throw new InvalidTypeException("A delegate property configuration was expected"); } if (context == null) { throw new InvalidTypeException("A sitecore data mapping context was expected"); } if (config.MapToCmsAction == null) { return; } config.MapToCmsAction(context); } public override object MapToProperty(AbstractDataMappingContext mappingContext) { SitecoreDelegatePropertyConfiguration config = this.Configuration as SitecoreDelegatePropertyConfiguration; SitecoreDataMappingContext context = mappingContext as SitecoreDataMappingContext; if (config == null) { throw new InvalidTypeException("A delegate property configuration was expected"); } if (context == null) { throw new InvalidTypeException("A sitecore data mapping context was expected"); } return config.MapToPropertyAction == null ? null : config.MapToPropertyAction(context); } public override bool CanHandle(AbstractPropertyConfiguration configuration, Context context) { return configuration is SitecoreDelegatePropertyConfiguration; } }
The Delegate Property Configuration
In order to configure properties in Glass Mapper, a property configuration is required. This allows you to define properties etc governing how the implementation of that property takes place during the mapping process.
using System; using Glass.Mapper; using Glass.Mapper.Configuration; public class DelegatePropertyConfiguration : AbstractPropertyConfiguration where T : AbstractDataMappingContext { public Action MapToCmsAction { get; set; } public Func MapToPropertyAction { get; set; } } using Glass.Mapper.Sc; public class SitecoreDelegatePropertyConfiguration : DelegatePropertyConfiguration { }
Making it work with Glass Fluent Configuration
In order for us to be able to get at the lovely new delegate functionality, we need to extend Glass Mapper’s default behaviour to include a new Delegate method.
using System; using System.Linq.Expressions; using Glass.Mapper.Sc.Configuration.Fluent; public static class SitecoreTypeExtensions { public static SitecoreDelegate Delegate(this SitecoreType type, Expression<Func> ex) { SitecoreDelegate sitecoreDelegate = new SitecoreDelegate(ex); type.Config.AddProperty(sitecoreDelegate.Configuration); return sitecoreDelegate; } }
Fluent Configuration
mySitecoreType.Delegate(x => x.Field).GetValue(y => MyMethod() // get the value from elsewhere);
This is awesome!
Pingback: Glass Mapper – Introducing Glass Maps | CardinalCore
Brilliant! Thanks a lot! For the sake of Glass beginners, you might want to add that SitecoreDelegateMapper needs to be registered in the container along with other/built-in Mappers. I don’t know if Glass has changed since you wrote this, but in current version (3.2.1.42) your code needs slight alterations to fit in. I’ve thrown up a Gist of the changes if anyone is interested. https://gist.github.com/bredstrup/1a7bed56a0360883fea3
Thanks again, Nat, this is a really awesome addon to Glass Mapper!