Constellation.Foundation.ModelMapping is an extremely lightweight framework that handles the transfer of Item field values to View Models. While the framework has only been public for about a year, its core programming style has its roots in predecessors to Constellation libraries, including Diamond and Synthesis.
Until now, the namespace has supported static access to a ModelMapper object. You’d use it like this:
var viewModel = ModelMapper.MapItemToNew<T>(item);
Because ModelMapper was static, I added some Extension Methods to Sitecore’s Item class, so you could be even more terse:
var viewModel = item.MapToNew<T>();
Frankly, I think this is a beautifully elegant bit of syntax candy, because it reads like a sentence and “hides” uninteresting concerns. However… It’s not very ASP.NET MVC-ish. It doesn’t support Dependency Injection, and therefore it cannot be abstracted and replaced with a mock-object for testing. After receiving the above bit of critique from a fellow Sitecore MVP at Symposium 2018, I set about correcting the issue.
New World Order
Constellation.Foundation.ModelMapping now requires Dependency Injection.
While there is still a ModelMapper class, it is no longer static. You have to make one. I’ve also created a contract (Interface) IModelMapper that allows you specify what instance of ModelMapper is created, and override mine with whatever your runtime requires.
At Sitecore Initialization the ModelMapping library will register an IModelMapper service pointing to Constellation.Foundation.ModelMapping.ModelMapper. This is done through Sitecore’s service configurator system, and can be adjusted via Sitecore *.config file patching. Here’s the default:
<services><configurator type="Constellation.Foundation.ModelMapping.ServicesConfigurator, Constellation.Foundation.ModelMapping"/>
Replacing the default configurator with yours is as simple as creating a class descending from Sitecore.DependencyInjection.IServicesConfigurator and referencing it as in the XML above.
If you want to see what’s in ModelMapping’s ServiceConfigurator, the source code is here.
Impacts to Coding Style
The first big impact will be the deprecation of the Item class Extension Methods. Because they infer an instance of ModelMapper, they’re no longer considered good citizens. Expect warnings from Resharper if you attempt to use them.
The second impact is that your Controllers will get a new argument in their Constructor.
public MyController(IModelMapper modelMapper)
this.LocalMapper = modelMapper;
Now, Mapping calls will always resemble the following examples:
T freshModel = LocalMapper.MapItemToNew<T>(someItem);
ICollection<T> modelCollection = LocalMapper.MapToCollectionOf<T>(items);
IEnumerable<T> models = LocalMapper.MapToEnumerableOf<T>(items);
It’s a little sad, but it keeps the TDD zealots at bay.
As of version 9.1.1.X (released January 18, 2019) you can still use the Item Extension Methods. Behind the scenes they ask the ServiceLocator for an appropriate instance of IModelMapper. If you currently have code that talks to the (formerly) static class ModelMapper, you’ll need to adjust your code as follows:
Nothing a little Find & Replace can’t handle. This shim allows you to retain backwards compatibility without having to visit every Controller or Repository object where you’re using static ModelMapper (but you should plan on a refactor in the near future).
I should mention that every Constellation library that uses ModelMapping internally has been modified to use Dependency Injection. It wasn’t particularly difficult, just time-consuming due to the fact that Constellation is a framework. There’s some interlocking parts that needed to be kept in lock-step so that the upgrade path was clear for folks that are using Constellation.
If you haven’t looked at Constellation’s ModelMapping library, you really should, particularly if you’re using a code-generating ORM today. ModelMapping was developed to be smaller, faster, and less complicated. You can read all about its philosophy, grab it off NuGet, or see the magic for yourself.