Initializing Kentico services with dependency injection

When developing MVC applications with Kentico, you may need to use some of the Kentico services. For example, services for logging activities, handling newsletter subscriptions, and more. The services are provided as part of the Kentico API, within the Kentico.Libraries NuGet integration package.

To use a service, you first need to create a service instance. We recommend instantiating Kentico 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 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 the MVC application 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 interfaces of Kentico 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:

  1. Register any custom service or repository types used on your website into your dependency injection container.

  2. Implement on-the-fly registration for all services that are part of the Kentico API (added via the Kentico.Libraries NuGet package).

    The implementation depends on your dependency injection container. On the basic level, get instances of services by calling: CMS.Core.Service.Resolve(serviceType)

The following example demonstrates registration of all Kentico 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 Kentico API
    builder.RegisterSource(new CMSRegistrationSource());

    // Resolves the dependencies
    DependencyResolver.SetResolver(new AutofacDependencyResolver(builder.Build()));
}





using System;
using System.Collections.Generic;
using System.Linq;

using CMS.Core;

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 Kentico API assemblies (included in the Kentico.Libraries NuGet package).
/// </summary>
public class CMSRegistrationSource : IRegistrationSource
{
    /// <summary>
    /// Gets whether the registrations provided by this source are 1:1 adapters on top of other components (I.e. 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(Service service, Func<Service, IEnumerable<IComponentRegistration>> 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
        object instance = null;
        if (CMS.Core.Service.IsRegistered(swt.ServiceType))
        {
            instance = CMS.Core.Service.Resolve(swt.ServiceType);
        }

        if (instance == null)
        {
            return Enumerable.Empty<IComponentRegistration>();
        }

        // Registers the service instance in the container
        return new[] { RegistrationBuilder.ForDelegate(swt.ServiceType, (c, p) => Service.Resolve(swt.ServiceType)).CreateRegistration() };
    }
}