Question

I have this class:

public class AutofacEventContainer : IEventContainer
    {
        private readonly IComponentContext _context;

        public AutofacEventContainer(IComponentContext context)
        {
            _context = context;
        }

        public IEnumerable<IDomainEventHandler<T>> Handlers<T>(T domainEvent)
                                               where T : IDomainEvent
        {
            return _context.Resolve<IEnumerable<IDomainEventHandler<T>>>();
        }
    }

The IEventContainer look like this:

public interface IEventContainer
    {

        IEnumerable<IDomainEventHandler<T>> Handlers<T>(T domainEvent)
            where T : IDomainEvent;
    }

Now this IEventContainer is used in a class DomainEvents like this:

public static class DomainEvents
    {

        ....................................
        ....................................

        public static IEventContainer Container;




        public static void Raise<T>(T domainEvent) where T : IDomainEvent
        {
            if (Container != null)
                foreach (var handler in Container.Handlers(domainEvent))
                    handler.Handle(domainEvent);

            // registered actions, typically used for unit tests.
            if (_actions != null)
                foreach (var action in _actions)
                    if (action is Action<T>)
                        ((Action<T>)action)(domainEvent);
        }


    }

My aim is to have the DomainEvents.Container registered so that all handlers are resolved.

 public class SomeModule : Module
   {
    protected override void Load(ContainerBuilder builder)
        {
            base.Load(builder);

            //Wrong way in Autofac
            //Because the following line is Ninject-like
            DomainEvents.Container = new AutofacContainer(componentContext);

           //What is the correct way to register it to achieve the above intent?

         }
    }

What is the way to do this in Autofac?

Was it helpful?

Solution

You are going to have a memory leak if you resolve out of the root scope. (See this post for a good overview of lifetime scopes in Autofac.) A better way to do it would be like this:

interface IEventRaiser
{
    void Raise<TEvent>(TEvent @event) where TEvent : IDomainEvent;
}

class AutofacEventRaiser : IEventRaiser
{
    private readonly ILifetimeScope context;

    public AutofaceEventRaiser(ILifetimeScope context)
    {
        this.context = context;
    }

    public void Raise<TEvent>(TEvent @event) where TEvent : IDomainEvent
    {
        using(var scope = context.BeginLifetimeScope("eventRaiser"))
        {
            foreach(var handler in scope.Resolve<IEnumerable<IDomainEventHandler<TEvent>>>())
            {
                handler.Handle(@event);
            }
        } // scope is disposed - no memory leak
    }
}


// then, in the composition root:

IContainer theContainer = BuildTheAutofacContainer();
DomainEvents.EventRaiser = new AutofacEventRaiser(theContainer);

This is the simple answer, but there is one more caveat you should be aware of...

The question is whether you really want to use a static IEventRaiser. DI purists will generally say, "no - you should inject an instance of your IEventRaiser into every class that needs one", but others have argued that a static event raiser is OK.

Be sure you are aware of how Autofac's lifetime scopes work before you make this decsion, because it could affect component sharing. For example, say SomeClass has an instance of SomeDependency and it raises SomeEvent. Let us also assume that SomeDependency is InstancePerLifetimeScope.

  • If SomeClass gets the IEventRaiser injected, then the handlers will be invoked on the same lifetime scope, and will be injected with the same instance of SomeDependency.
  • If SomeClass uses a static IEventRaiser, then the handlers will be invoked on a different lifetime scope, and will be injected with a different instance of SomeDependency.

You can see why this matters if you imagine that SomeDependency is something like a DB transaction.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top