Tuesday, October 11, 2022

Sitecore Processors and Dependency Injection

 This is a copy of my blog post originally posted here to keep all things in one place.

All current projects use dependency injection(DI). It gives you a lot of benefits:

  • It decreases coupling between classes and their dependencies.
  • It increases the testability of code. It is much easier to test code without dependencies.
  • It increases maintainability. If required, you may rewrite just one implementation without touching the implementations of all consumers.
  • It increases reusability. The implementation could be used in different places.
  • It allows concurrent development.

I haven’t seen for many years Sitecore projects without DI. Everyone uses DI.


Sitecore allows the usage of DI inside processors. All that you need, is to add resolve="true" to your processor configuration. But there is one not-obvious thing. The default lifetime of Sitecore processors is Singleton. It may cause an issue.

For example, you have a processor with dependency:

public class TestProcessor 
{
    private IDependency _dependency;
    public TestProcessor(IDependency dependency)
    {
        _dependency = dependency;
    }
    public void Process()
    {
        //usage of _dependency
    }
}
<processor type="YourNamespaceTestProcessor, YourAssembly" resolve="true" />

Dependency has a Transient lifetime.

[Service(typeof(IDependency), Lifetime = Lifetime.Transient)]
public class Dependency : IDependency
{
}

public interface IDependency
{
}

What lifetime will _dependency have inside TestProcessor? The answer is not obvious, it will have Singleton lifetime. It happens because the default lifetime for Sitecore processors is Singleton. It makes sense for performance optimization. You don’t want to construct new objects if you can use the same ones. That is why your Transient configuration will not matter. And you will get “cached” _dependency from your Singleton TestProcessor.

There are 2 options, for how you can use a different lifetime of dependencies inside your process.

The first is the usage of ServiceLocator. You may modify your processor in this way.

public class TestProcessor 
{
    public TestProcessor()
    {
    }
    public void Process()
    {
        var dependency = ServiceLocator.ServiceProvider.GetService<IDependency>();
        //usage of _dependency
    }
}

In this case, you will get dependency that is not cached by the lifetime of the processor. But usually, usage of ServiceLocator is a bad practice.

The second option is the usage of a not-well-documented reusable attribute in your processor configuration.

<processor type="YourNamespaceTestProcessor, YourAssembly" resolve="true" reusable="false" />

reusable attribute has a default value true and is responsible for switching processor scope from Processor to Invocation.

private static ProcessorObject GetObjectFromType(XmlNode processorNode)
{
	string attribute = XmlUtil.GetAttribute("reusable", processorNode, "true");
	ObjectScope scope = ((attribute == "true") ? ObjectScope.Processor : ObjectScope.Invocation);
	object obj = Factory.CreateObject(processorNode, assert: true);
	return new ProcessorObject(obj, scope);
}

Conclusion

Be careful, when you are working with dependency injection in Sitecore processors, and remember about their default lifetime.

No comments:

Post a Comment