Following my previous article about Dependency injection in Sitecore custom commands, I think it is only appropriate to continue with something similar for Sitecore event handlers. And when I say similar I mean very similar - in fact reading this article after having read the previous one may induce some sense of deja vu :-) To learn more about Sitecore events visit Using Events.
This article assumes an understanding of Sitecore events and the concept of dependency injection. The purpose is to show how to use dependency injection in Sitecore events.
The normal way of creating an event handler for a Sitecore event is to create a handler class with an EventHandler delegate, i.e. a method with the EventHandler signature, and then add some config to Sitecore's <events> section defining where to find the event handler implementation, so that Sitecore can instantiate the event handler and trigger the delegate when the event occurs.
The problem with this approach is that nowadays it is common to use dependency injection in software solutions, and letting Sitecore take care of creating instances of your custom code means that you loose the possibility of injecting the needed dependencies. Luckily there is also a way out of this morass.
Sitecore has created a class called Event, which is used for subscribing, unsubscribing, and raising events. The good news is that it is available for use.
So here is a suggestion on how to use it to obtain dependency injection in Sitecore events. It is based on using Autofac as IoC container.
First, create a base class for your event handlers:
namespace TestApp.Events { public abstract class BaseEventHandler { public string FullName { get; private set; } protected BaseEventHandler(string fullName) { FullName = fullName; } public abstract void OnEvent(object sender, System.EventArgs args); } }This base class defines the EventHandler delegate method signature that all derived event handler classes must implement, but it also has one property, FullName, for holding the event name for registration purposes.
Next, create your event handler inheriting from BaseEventHandler like this:
namespace TestApp.Events { public class MyEventHandler : BaseEventHandler { private readonly IMyDependency _myDependency; public MyEventHandler(string fullName, IMyDependency myDependency) : base(fullName) { _myDependency = myDependency; } public override void OnEvent(object sender, System.EventArgs args) { // event handler implementation } } }As you can see we inject a dependency in the constructor. The constructor furthermore calls the base constructor to set the event name.
Now, create a class for registering events using the Sitecore Event class:
namespace TestApp.Events { public static class EventConfigurator { public static void Configure(System.Collections.Generic.IEnumerable<BaseEventHandler> eventHandlers) { foreach (var eventHandler in eventHandlers) { Sitecore.Events.Event.Subscribe(eventHandler.FullName, eventHandler.OnEvent); } } } }The Configure method takes a collection of BaseEventHandler objects (our event handler instances), then uses Subscribe method on the Event class to subscribe the events.
That is basically all the pieces we need. We just have to fit everything together in our bootstrapper (the place where all the dependencies are set up using the IoC container). This could look something like this:
... var builder = new ContainerBuilder(); builder.RegisterType<MyDependency>().As<IMyDependency>().InstancePerLifetimeScope(); builder.RegisterType<MyEventHandler>().As<BaseEventHandler>().WithParameter("fullName", "mynamespance:mycategory:myevent").InstancePerLifetimeScope(); var rootContainer = builder.Build(); var eventHandlers = rootContainer.Resolve<IEnumerable<BaseEventHandler>>(); EventConfigurator.Configure(eventHandlers); ...So we just register dependencies as usual. The new thing is that we now register our event handlers in code, instead of using a Sitecore config file. And then we call our EventConfigurator with a collection of instances of all our event handlers.
That's it. Plain and simple :-)
Update:
Please note that since the event handlers are resolved only once (at app startup), any injected dependencies are effectively singletons.