Dependency injection recipe for Episerver developer

Choosing proper dependency injection type might be hard. But it is not so hard when you have a recipe.

Ingredients

Method

  1. Find out if the dependent object supports constructor injection.

    TIP: Your custom classes, controllers, scheduled jobs (Episerver 10.3+) supports constructor injection. You also can check what does not support constructor injection here (it might not be up to date).

  2. Use constructor injection if supported.

  3. If constructor injection is not available, check if there is a method injection available. For example, when implementing IInitializableModule, there is a context available as a method argument to the Initialize method. The context has a property of Service Locator available called Locate. You can get different Episerver services from it or use the Advanced property to get any dependency you need.

    public void Initialize(InitializationEngine context)
    {
      if (_initialized)
      {
          return;
      }
    
      var loader = context.Locate.ContentLoader();
      var service = context.Locate.Advanced.GetInstance<IMyService>();
    }
    

    While Service Locator is an anti-pattern, initialization modules are the composition root here. So it is okay to use it.

  4. If method injection is not available, use either Service Locator or Injected property. Both are same - Injected property uses Service Locator under the hood. Choose what you like better. The only rule - be consistent within your codebase.

    TIP: In case of 3. and 4. try to "locate" only one dependency which is the entry point for your logic and which uses constructor injection to get all needed services.

    public class EntryPoint
    {
      public EntryPoint(IContentLoader contentLoader, ReferenceConverter referenceConverter)
      {
          // Initialization code here
      }
    
      public void Execute()
      {
          // Run your logic here.
      }
    }
    
    public void Initialize(InitializationEngine context)
    {
      if (_initialized)
      {
          return;
      }
    
      var service = context.Locate.Advanced.GetInstance<EntryPoint>();
      service.Execute();
    }
    

    Here you can see that the only dependency we are locating in the initializable module is EntryPoint. Then EntryPoint uses constructor injection to get all the needed services.