Initializing Xperience services with dependency injection
When developing applications with Xperience, you may need to use some of the services provided by the system. For example, services for logging activities, handling newsletter subscriptions, and more. The services are provided as part of the Xperience API, within the Xperience.Libraries NuGet integration package.
To use a service, you first need to create a service instance. We recommend instantiating Xperience services via dependency injection.
Tip: If you are not familiar with the dependency injection design pattern in general, we recommend that you learn about it first. For example, you can start with the following articles:
- Dependency Injection by Steve Smith
- Dependency Injection and Inversion of Control with ASP.NET MVC by Mike Brind
Dependency injection design pattern
Dependency injection is a design pattern that is common to many programming languages and environments. Instead of instantiating a service instance inside a class constructor, you can pass the service instance into the constructor as a parameter. This way classes (e.g. controllers) do not need to obtain and configure the instance of a class they depend on.
The following code snippet shows a general example of the dependency injection design approach. Notice that the controller constructor accepts an instance of a service created by the dependency injection container, without the need to instantiate explicitly.
public class AccountController : Controller
{
private readonly IMembershipActivityLogger mMembershipActivityLogger;
public AccountController(IMembershipActivityLogger membershipActivityLogger)
{
mMembershipActivityLogger = membershipActivityLogger;
}
...
}
Developing applications with the dependency injection pattern in mind provides the following benefits:
- Less maintenance – changing object implementation is easier without strong coupling.
- Easier testing – when writing tests for MVC controllers, the controllers can be tested in isolation from the dependent objects.
- Loose coupling – classes are not directly dependent on one another.
Dependency injection containers
You can use a dependency injection container to instantiate Xperience services. The purpose of dependency injection containers is to create a mapping between interfaces and concrete types. By using these containers, you reduce the complexity of instantiating various services and resolving the dependencies between them.
Some of the common dependency injection containers include:
For example, our sample Dancing Goat MVC project uses the Autofac container to resolve dependencies.
Initializing dependency injection containers
If you want to use the dependency injection design pattern for development, we recommend the following approach:
Register any custom service or repository types used on your website into your dependency injection container.
Implement on-the-fly registration for all services that are part of the Xperience API (added via the Kentico.Xperience.Libraries NuGet package).
The implementation depends on your dependency injection container. On the basic level, you can get instances of services by calling: CMS.Core.Service.Resolve(serviceType) or CMS.Core.Service.ResolveOptional(serviceType)
The following example demonstrates registration of all Xperience API services using Autofac:
using System.Web.Mvc;
using Autofac;
using Autofac.Integration.Mvc;
...
private void ConfigureDependencyResolver()
{
// Initializes the Autofac builder instance
var builder = new ContainerBuilder();
// Adds a custom registration source (IRegistrationSource) that provides all services from the Xperience API
builder.RegisterSource(new CMSRegistrationSource());
// Resolves the dependencies
DependencyResolver.SetResolver(new AutofacDependencyResolver(builder.Build()));
}
using System;
using System.Collections.Generic;
using System.Linq;
using Autofac.Core;
using Autofac.Builder;
/// <summary>
/// Allows registrations to be made on-the-fly when unregistered services are requested.
/// Provides all services from the Xperience API assemblies (included in the Kentico.Xperience.Libraries NuGet package).
/// </summary>
public class CMSRegistrationSource : IRegistrationSource
{
/// <summary>
/// Indicates whether registrations provided by this source are 1:1 adapters on top of other components (like Meta, Func or Owned).
/// </summary>
public bool IsAdapterForIndividualComponents => false;
/// <summary>
/// Retrieves registrations for an unregistered service, to be used by the container.
/// </summary>
/// <param name="service">The service that was requested.</param>
/// <param name="registrationAccessor">A function that will return existing registrations for a service.</param>
public IEnumerable<IComponentRegistration> RegistrationsFor(Autofac.Core.Service service, Func<Autofac.Core.Service, IEnumerable<ServiceRegistration>> registrationAccessor)
{
// Checks whether the container already contains an existing registration for the requested service
if (registrationAccessor(service).Any())
{
return Enumerable.Empty<IComponentRegistration>();
}
// Checks that the requested service carries valid type information
var swt = service as IServiceWithType;
if (swt == null)
{
return Enumerable.Empty<IComponentRegistration>();
}
// Gets an instance of the requested service using the CMS.Core API
// Returns null if the service instance cannot be resolved
object instance = CMS.Core.Service.ResolveOptional(swt.ServiceType);
if (instance == null)
{
return Enumerable.Empty<IComponentRegistration>();
}
// Registers the service instance in the container
return new[] { RegistrationBuilder.ForDelegate(swt.ServiceType, (c, p) => instance).CreateRegistration() };
}
}