Possibly Simplest Way Of Enabling Dependency Injection In Sitecore Controllers and ApiControllers

This is, yet, another blog post on the topic of dependency injection with Sitecore Controller Renderings as well as API Controllers.  Let me start by saying that the most helpful blog post I’ve read on the topic is by Kam Figyhttps://kamsar.net/index.php/2016/08/Dependency-Injection-in-Sitecore-8-2/.  I encourage you to read this blog post because it is still applicable to Sitecore 9.

My inspiration for this blog post came from the fact that when you Google the terms “sitecore dependency injection”, one of the first results is the official Sitecore documentation on the topic – https://doc.sitecore.net/sitecore_experience_platform/developing/developing_with_sitecore/dependency_injection.

So you go to this page and read about the different ways that you can register your dependencies, service resolution, viewing your dependency injection configuration, etc…  And you think, “hmm… looks like all of the info is here.  Let me give this is a try”.  And you go through everything, and it doesn’t work.

I’m going to try and show you the easiest way possible to implement dependency injection following Sitecore’s official documentation and I’m going to point out the missing pieces of information.  And we’re going to start from scratch – absolute scratch!  For this walkthrough, I’m assuming you haven’t even installed Sitecore yet.

The steps:

  1. Install Sitecore
    1. I’m using 9.0.2 but you can use any version starting at 8.2, I believe.
  2. Login to Sitecore
  3. Create an MVC Layout and call it My Solution Main Layout
    1. I placed my layout in the following folder structure
      1. \Views\_Layouts\MySolution\MySolutionMainLayout.cshtml
  4. Create a controller rendering in Sitecore and specify the Controller and Action
    1. for my example, I created a Generic Callout
  5. Right-click Home and create a Sample Item
  6. Update the presentation details of the item you created to use the new Layout and Controller Rendering you created
  7. Push the item through workflow
  8. Publish the site
  9. Go to this item in a new tab
    1. You should get an error saying something like,  “Sitecore can’t find the controller”
  10. Create a solution in Visual Studio
  11. Create a web app
    1. I called it Feature.GenericCallout
    2. I chose the empty web app template
  12. Install the following NuGet packages
    1. Sitecore.Kernel.NoReferences (whatever version matches your instance of Sitecore)
    2. Microsoft.AspNet.Mvc (whatever version matches your instance of Sitecore)
  13. Set the build action of the main web.config to None
    1. so you don’t accidentally deploy your vanilla MVC web.config and destroy your Sitecore site
  14. Create a Services folder
    1. Add an interface and a class
      1. I called my interface and class, IGenericCalloutServiceGenericCalloutService, respectively
  15. Create a Controllers folder
    1. Add a Controller
      1. I called my controller GenericCalloutController
      2. Add a private variable of type IGenericCalloutService
      3. Add a constructor that takes in a parameter of type IGenericCalloutService and set the private variable
        1. You know, inject the dependency
        2. Make sure this is the only constructor in your Controller
      4. Add a method to the controller called Default and return the View
  16. Create a Views folder
  17. In there, create a folder called GenericCallout
  18. In there, create a partial view and name it Default.cshtml
    1. I just added this to my View to make sure it was deploying, <h2>Generic Callout</h2>
  19. Deploy this project to your local Sitecore site
  20. Reload the Sample Item page
    1. You should now get an error saying something like:
      1. Constructor on type ‘Feature.GenericCallout.Controllers.GenericCalloutController’ not found.

At this point, your solution should look something like this:

my-solution-generic-callout-part-1
I’ve removed some elements of the solution that we haven’t talked about yet.  The next screenshots will show these elements.

This is when we start following the Sitecore documentation.

The first thing we have to do is register our dependencies.  I’m going with the option of Registration in configuration.

The steps:

  1. Add an App_Config folder
  2. In there, add an Include folder
  3. In there, add a config file
    1. I called my config, Feature.GenericCallout.config
    2. Add the following XML to your config:
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <services>
            <register serviceType="Feature.GenericCallout.Services.IGenericCalloutService, Feature.GenericCallout"
implementationType="Feature.GenericCallout.Services.GenericCalloutService, Feature.GenericCallout" />
        </services>
    </sitecore>
</configuration>

As described on the Sitecore documentation page, this config will register your concrete implementation of your interface with the class you created.

Deploy this config to your site and reload your page.

At this point, I thought, “this should work!”  To my surprise, it didn’t.  I still got the same error:

Constructor on type ‘Feature.GenericCallout.Controllers.GenericCalloutController’ not found.

What was I missing?  So I continued reading the Sitecore documentation.  In the next section, Service resolution, it talks about how you can inject dependencies into processors, which is really cool and there’s a line that says:

To use dependency injection in an MVC controller, add this processor to the pipeline:<processor type=”Sitecore.Mvc.Pipelines.Loader.InitializeDependencyResolver, Sitecore.Mvc”/>

So I thought, “that must be the processor I’m missing”.  So I went to my /sitecore/admin/showconfig.aspx page to confirm that this processor was missing but it was there!

So that was not my problem.  So what could be the problem?  This is when I went to Kim’s blog post again.  Remember at the beginning I said you should read it?

In his blog post, Kim mentions that:

The controller class must be registered with the container to be resolvable (e.g. you must register it such as container.AddTransient<FooController>()).

So I thought, “worth a try”.  But where does this code go?  This code goes into a Sitecore Services Configurator class.

The steps:

  1. Add a class to the project called GenericCalloutServicesConfigurator.cs
  2. Have the class implement Sitecore.DependencyInjection.IServicesConfigurator
    1. you will need to include this NuGet package as well – Microsoft.Extensions.DependencyInjection.Abstractions
  3. In the Configure method, add a line for your Controller
    1. serviceCollection.AddTransient(typeof(GenericCalloutController));
      1. notice I’m not calling the exact same method Kim is calling.

Your class should look something like this:

using Feature.GenericCallout.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Sitecore.DependencyInjection;

namespace Feature.GenericCallout
{
    public class GenericCalloutServicesConfigurator : IServicesConfigurator
    {
        public void Configure(IServiceCollection serviceCollection)
        {
            serviceCollection.AddTransient(typeof(GenericCalloutController));
        }
    }
}

And to get this class to run, we need to patch it into the services section of the Sitecore configuration:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <services>
            <configurator type="Feature.GenericCallout.GenericCalloutServicesConfigurator, Feature.GenericCallout"/>
            <register serviceType="Feature.GenericCallout.Services.IGenericCalloutService, Feature.GenericCallout"
                      implementationType="Feature.GenericCallout.Services.GenericCalloutService, Feature.GenericCallout" />
        </services>
    </sitecore>
</configuration>

Deploy your code to your site again, reload your page and… voilà!  It worked!  That was the missing piece!  Why this is not called out on the Sitecore documentation page, I’m not sure, but this piece is very important.

One thing to clarify, in my example above, I am using the AddTransient method.  This is one of 3 methods you can use:

  1. AddScoped
    1. scoped objects are the same within a request, but different across different requests.
  2. AddSingleton
    1. singleton objects are the same for every object and every request.
  3. AddTransient
    1. transient objects are always different; a new instance is provided to every controller and every service.

You can find more information about this on this StackOverflow answer – https://stackoverflow.com/a/38139500/195335.

Finally, I wanted to see how easy it would be to enable dependency injection in an API controller.

The steps:

  1. Install NuGet package Microsoft.AspNet.WebApi
    1. This package will install NuGet package Newtonsoft.Json
      1. You’ll need to update this NuGet package to the version that matches the version that came with your instance of Sitecore.  To find out your version of Newtonsoft:
        1. Go to \website\bin
        2. Right-click Newtonsoft.Json.dll
        3. Go to the Details tab
  2. Create a Controller in the Controllers folder
    1. I called my controller GenericCalloutApiController.cs

My Api controller looks like this:

using Feature.GenericCallout.Services;
using System.Web.Http;

namespace Feature.GenericCallout.Controllers
{
    public class GenericCalloutApiController : ApiController
    {
        private IGenericCalloutService _genericCalloutService;

        public GenericCalloutApiController(IGenericCalloutService genericCalloutService)
        {
            _genericCalloutService = genericCalloutService;
        }

        [Route("my-solution-api/feature/generic-callout")]
        public string GetGenericCallout()
        {
            return "This is the response.";
        }
    }
}

And, since I know I had to register my “regular” controller, I’m going to go ahead and register my API controller as well.  So that will look like this:

using Feature.GenericCallout.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Sitecore.DependencyInjection;

namespace Feature.GenericCallout
{
    public class GenericCalloutServicesConfigurator : IServicesConfigurator
    {
        public void Configure(IServiceCollection serviceCollection)
        {
            serviceCollection.AddTransient(typeof(GenericCalloutController));
            serviceCollection.AddTransient(typeof(GenericCalloutApiController));
        }
    }
}

To test this, I wrote some JavaScript using jQuery to make an AJAX call to my API controller.  I didn’t have to do this – I could’ve tested it by simply requesting the API route in a browser tab.

Here’s the super basic JavaScript I wrote:

$(function () {
    $.get('/my-solution-api/feature/generic-callout', function (response) {
        console.log(response);
    });
});

This also worked as expected!

If you’ve been following this post and building your solution as I described above, your solution should now look something like this:

my-solution-generic-callout-part-2

So, to summarize, we were able to get dependency injection working following the Sitecore documentation with one additional detail – registering our controllers.

I hope this helps you in your adventures with dependency injection with Sitecore.

6 thoughts on “Possibly Simplest Way Of Enabling Dependency Injection In Sitecore Controllers and ApiControllers

  1. Amazing!! This is the best article written for DI in Sitecore. Apt to the Title “POSSIBLY SIMPLEST WAY OF ENABLING DEPENDENCY INJECTION IN SITECORE CONTROLLERS AND APICONTROLLERS”

    Like

  2. In real life scenario the Application will have multiple Controller, could you please suggest how to achieve registration for multiple Controllers?

    Thanks,
    Debaparna

    Like

    1. Right. The way I’ve done it is, by referencing the multiple Controllers (and their dependencies) in the various files I mentioned in the post. So you need to register each of your individual services and controllers in the configs and classes I mentioned in the post.

      I’ve come across a few solutions that just scan your assemblies for classes that match a certain naming convention, such as, “…Controller”, “…Service”, and then automatically register those.

      I understand the benefits of that approach but I prefer to define these registration explicitly.

      Like

  3. I am getting the below error.”ExceptionMessage”:”An error occurred when trying to create a controller of type ‘GenericCalloutApiController’. Make sure that the controller has a parameterless public constructor.”

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s