Search code examples
c#genericsautofacprojectioneventhandler

How to resolve different implementations of generic event handlers with Autofac


I am new to Autofac and I cannot have an example of event subscriber application working. Could you help me model it?

The idea is the following.

I have a REST API controller where clients can POST events (implementations of IDomainEvent)

The controller needs to receive injected ALL the projector classes (event handlers) that will do something with the events.

public class EventsController 
    : Controller
{
    private readonly IEnumerable<IEventHandler> _projectors;

    public EventsController(IEnumerable<IEventHandler> projectors)
    {
        _projectors = projectors;
    }

    [HttpPost("EventA")]
    public IActionResult AddEventA([FromBody]EventA evt)
    {
        foreach (var projector in _projectors)
        {
            projector.Handle(evt);
        }
        return Ok();
    }

    [HttpPost("EventB")]
    public IActionResult AddEventB([FromBody]EventB evt)
    {
        foreach (var projector in _projectors)
        {
            projector.Handle(evt);
        }
        return Ok();
    }
}

And then I have many Projectors (event handlers) that do different things with the events they subscribe to.

public class ProjectorA : IEventHandler<EventA>, IEventHandler<EventB>
{
    private readonly IRepository _repository;

    public ProjectorA(IRepository repository)
    {
        _repository = repository;
    }

    public void Handle(EventA evt)
    {
        // do something with EventA
    }

    public void Handle(EventB evt)
    {
        // do something with EventB
    }
}

public class ProjectorB : IEventHandler<EventA>
{
    private readonly IRepository _repository;

    public ProjectorA(IRepository repository)
    {
        _repository = repository;
    }

    public void Handle(EventA evt)
    {
        // do something with EventA
    }

    // I don't care about EventB so I don't subscribe to it
}

The IEventHandler would be something like this I guess:

public interface IEventHandler<in TDomainEvent>
    : IEventHandler
    where TDomainEvent : IDomainEvent
{
    void Handle(TDomainEvent evt);
}

How to modify this modelling and how to register my projectors in Autofac to use it in a similar way?

Thanks in advance!


Solution

  • If you have only 2 types of Event the easiest way would be to have 2 dependencies

    public class EventsController 
        : Controller
    {
        private readonly IEnumerable<IEventHandler<EventA>> _Aprojectors;
        private readonly IEnumerable<IEventHandler<EventB>> _Bprojectors;
    
        public EventsController(IEnumerable<IEventHandler<EventA>> Aprojectors,
                                IEnumerable<IEventHandler<EventB>> Bprojectors)
        {
            _Aprojectors = Aprojectors;
            _Bprojectors = Bprojectors;
        }
    
        // ...
    }
    

    But if you want a generic solution, the easiest way is to create a Generic Repository

    public class EventsController<TEvent> 
        : Controller
        where TEvent : IEvent 
    {
        private readonly IEnumerable<IEventHandler<TEvent>> _projectors;
    
        public EventsController(IEnumerable<IEventHandler<Event>> projectors)
        {
            _projectors = projectors;
        }
    
        [HttpPost()]
        public IActionResult AddEvent([FromBody]TEvent evt)
        {
            foreach (var projector in _projectors)
            {
                projector.Handle(evt);
            }
            return Ok();
        }
    }
    

    and declare your EventHandler like this

    builder.RegisterType<ProjectorA>().As<IEventHandler<AEvent>>();
    builder.RegisterType<ProjectorB>().As<IEventHandler<BEvent>>();
    

    Generic controller is not implemented by default within ASP.net MVC. For ASP.net core, this post https://stackoverflow.com/a/45698048/814735 contains great explanation on how to implement such a generic controller.