HomeDev GuideAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunitySubmit a ticketLog In
GitHubNuGetDev CommunitySubmit a ticket

Dependency injection

Describes how to use dependency injection in Optimizely Content Management System (CMS 10 and 11).

Dependency injection in Optimizely Customized Commerce is defined in an abstract API in namespace EPiServer.ServiceLocation within the EPiServer.Framework package. There is an implementation of the abstraction based on StructureMap in package EPiServer.ServiceLocation.StructureMap. It is possible to use either StructureMap directly or use Optimizely's StructureMap abstractions when configuring the IOC container. The recommended approach is to use the abstractions documented in this topic.

Implicit registration of services

Below are examples of using implicit registration. To accomplish this, use the ServiceConfiguration attribute, which makes it easier to read classes since the registration is done in the same class as the implementation.
This code is an example of registering a service, either as a transient service (new instances are created every time), or as a singleton service (a single instance is re-used).

public interface IService
        {
            void Sample();
        }
        [ServiceConfiguration(ServiceType = typeof(IService))]
        public class TransientService : IService
        {
            public void Sample()
            {
                throw new NotImplementedException();
            }
        }
        [ServiceConfiguration(ServiceType = typeof(IService), Lifecycle = ServiceInstanceScope.Singleton)]
        public class SingletonService : IService
        {
            public void Sample()
            {
                throw new NotImplementedException();
            }
        }

Explicit registration of services

The code below provides examples of registering IService and its implementation Service using the CMS abstractions. The abstractions are available in initialization modules which implement the IConfigurableModule interface. Inline comments describe the examples in more detail:

[InitializableModule]
        public class ModuleWithServices : IConfigurableModule
        {
            public void ConfigureContainer(ServiceConfigurationContext context)
            {
                //Register IService1 with implementation Service1, create new instance every time
                context.Services.AddTransient<IService, Service>();
                //Register IService1 with implementation Service1, re-use a single instance
                context.Services.AddSingleton<IService, Service>();
                //Register IService1 with custom factory
                context.Services.AddTransient<IService>((locator) => new Service());
                //Register IService1 to be cached per HTTP request or per thread if no HTTP request exist
                context.Services.AddHttpContextOrThreadScoped<IService, Service>();
            }
            public void Initialize(InitializationEngine context)
            {
            }
            public void Uninitialize(InitializationEngine context)
            {
            }
        }

Use StructureMap configuration

If the package EPiServer.ServiceLocation.StructureMap is used as dependency injection implementation, it is possible to configure the IOC container using the StructureMap API instead of the CMS API. The recommendation is to use the CMS API, unless some specific StructureMap feature not exposed in Episerver API is used. That way it is easier to replace StructureMap with another IOC implementation in the future if that need occurs. To use direct StructureMap configuration, use the StructureMap extension method, which returns an instance of StructureMap.IContainer.  Then the configuration can be done directly against StructureMap.

using EPiServer.Framework;
    using EPiServer.Framework.Initialization;
    using EPiServer.ServiceLocation;
    using EPiServer.ServiceLocation.Compatibility;
    namespace EPiServerSamples
    {
       [InitializableModule]
       public class ModuleWithServices : IConfigurableModule
       {
          public void ConfigureContainer(ServiceConfigurationContext context)
            {
                //Use extension method StructureMap() to access StructureMap.IContainer
                context.StructureMap().Configure(c =>
                {
                    c.For().Use();
                });
    
            }
            public void Initialize(InitializationEngine context)
            {
            }
            public void Uninitialize(InitializationEngine context)
            {
            }
       }
    }

ServiceLocator

To get an instance of a service without using dependency injection, use the ServiceLocator class and its Current property. Example:

var myService = ServiceLocator.Current.GetInstance<IService>();

Intercept existing services

There are occations when it is useful to intercept calls to a service, for example, to debug or to add logging. By using the IServiceConfigurationProvider.Intercept<T> method, it is possible to replace existing services with another implementation. The provided factory method will have access to the default instance of the service.

If there are several implementations registered for a service, all registrations are intercepted. The interceptor service has the same scope as the intercepted service was registered with.

This example shows an interceptor registration of the ISynchronizedObjectInstanceCache where the interceptor logs remote removals.

using EPiServer.Framework;
    using EPiServer.Framework.Cache;
    using EPiServer.Framework.Initialization;
    using EPiServer.Logging;
    using EPiServer.ServiceLocation;
    using System;
    
    namespace EPiServerSamples
    {
        [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
        public class LoggingInitializer : IConfigurableModule
        {
            public void ConfigureContainer(ServiceConfigurationContext context)
            {
                context.Services.Intercept<ISynchronizedObjectInstanceCache>(
                    (locator, defaultCache) => new LoggingSynchronizedCache(defaultCache));
            }
            public void Initialize(InitializationEngine context) { }
            public void Uninitialize(InitializationEngine context) { }
        }
        public class LoggingSynchronizedCache : ISynchronizedObjectInstanceCache
        {
            private readonly ISynchronizedObjectInstanceCache _defaultCache;
            private readonly ILogger _log = LogManager.GetLogger(typeof(LoggingSynchronizedCache));
    
            public LoggingSynchronizedCache(ISynchronizedObjectInstanceCache defaultCache)
            {
                _defaultCache = defaultCache;
            }
            public void RemoveRemote(string key)
            {
                if (_log.IsDebugEnabled())
                    _log.Debug($"Remote removal called at '{DateTime.Now}' with key '{key}'");
    
                _defaultCache.RemoveRemote(key);
            }
            public IObjectInstanceCache ObjectInstanceCache => _defaultCache.ObjectInstanceCache;
            public FailureRecoveryAction SynchronizationFailedStrategy
            {
                get { return _defaultCache.SynchronizationFailedStrategy; }
                set { _defaultCache.SynchronizationFailedStrategy = value; }
            }
            public object Get(string key) => _defaultCache.Get(key);
            public void Insert(string key, object value, CacheEvictionPolicy evictionPolicy)
                => _defaultCache.Insert(key, value, evictionPolicy);
            public void Remove(string key) => _defaultCache.Remove(key);
            public void RemoveLocal(string key) => _defaultCache.RemoveLocal(key);
        }
    }

Integrate with MVC 5 dependency resolver

Templates developed on CMS can set IServiceLocator to resolve dependencies in controllers in MVC 5. The following code provides an example of integrating IServiceLocator. This sample is used in the Alloy sample templates.

[InitializableModule]
        [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
        public class DependencyResolverInitialization : IConfigurableModule
        {
            public void ConfigureContainer(ServiceConfigurationContext context)
            {
            }
            public void Initialize(InitializationEngine context)
            {
                DependencyResolver.SetResolver(new ServiceLocatorDependencyResolver(context.Locate.Advanced));
            }
            public void Uninitialize(InitializationEngine context)
            {
            }
            public void Preload(string[] parameters)
            {
            }
        }

    public class ServiceLocatorDependencyResolver : IDependencyResolver
        {
            readonly IServiceLocator _serviceLocator;
    
            public ServiceLocatorDependencyResolver(IServiceLocator serviceLocator)
            {
                _serviceLocator = serviceLocator;
            }
    
            public object GetService(Type serviceType)
            {
                if (serviceType.IsInterface || serviceType.IsAbstract)
                {
                    return GetInterfaceService(serviceType);
                }
                return GetConcreteService(serviceType); 
            }
    
            private object GetConcreteService(Type serviceType)
            {
                try
                {
                    // Can't use TryGetInstance here because it won’t create concrete types
                    return _serviceLocator.GetInstance(serviceType);
                }
                catch (ActivationException)
                {
                    return null;
                }
            }
    
            private object GetInterfaceService(Type serviceType)
            {
                object instance;
                return _serviceLocator.TryGetExistingInstance(serviceType, out instance) ? instance : null;
            }
    
            public IEnumerable<object> GetServices(Type serviceType)
            {
                return _serviceLocator.GetAllInstances(serviceType).Cast<object>();
            }
        }