Let’s say I want to use the new (brilliant) ISelectionFactory to easily create a list of something for the editor to choose. The data the editor uses is fetched from EPiServers IContentRepository.
public class SomeDataSelectionFactory : ISelectionFactory { readonly IContentRepository _content; public SomeDataSelectionFactory(IContentRepository content) { _content = content; } public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata) { return _content .GetChildren<PageData>(ContentReference.StartPage) .Select(c => new SelectItem { Value = c.ContentLink.ID, Text = c.PageName }); } }
Unfortunately this won’t work since the class is not created from the container. Instead we get an error in edit mode saying the page that has a property that uses the selection factory can’t be previewed and if we follow the link in the javascript console log that reports a 500 error we find the expected “No parameterless constructor defined for this object”.
Service Locator
Since constructor injection doesn’t work we can use service location, either via the ServiceLocator or Injected.
public class SomeDataSelectionFactory : ISelectionFactory { public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata) { return ServiceLocator.Current.GetInstance<IContentRepository>() .GetChildren<PageData>(ContentReference.StartPage) .Select(c => new SelectItem { Value = c.ContentLink.ID, Text = c.PageName }); } }
public class SomeDataSelectionFactory : ISelectionFactory { Injected<IContentRepository> _content; public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata) { return _content .Service .GetChildren<PageData>(ContentReference.StartPage) .Select(c => new SelectItem { Value = c.ContentLink.ID, Text = c.PageName }); } }
These two options are basically equivalent since Injected
The test
When writing a test for the selection factory in the first example it become very clear that I need to supply a IContentRepository because of the constructor. The service locator based approaches happily create the object but will fail at runtime because it can’t resolve the dependency to IContentRepository. This means I need to make sure that before the test is run I have set a service locator and make sure that the relevant dependencies are registered.
var container = new Container(); container.Configure(x => x.For<IContentRepository>().Use(contentRepository)); ServiceLocator.SetLocator(new StructureMapServiceLocator(container));
This is not complicated to do but that’s beside the point. When a new up something I can clearly see what the object needs in the constructor. Service location hides all those details away. That’s not good infrastructure magic that just works. That’s magic where I need to know a lot about the involved rabbits and hats and that’s one reason why I don’t like the service locator.