Sitecore Processors and Dependency Injection
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.