W
W
WhiteNinja2017-03-24 13:59:13
C++ / C#
WhiteNinja, 2017-03-24 13:59:13

C#: How to correctly configure Autofac for Domain events dispatcher?

Good afternoon!
When implementing Domain events, I use
the event itself:

public class ProductCreatedEvent : IDomainEvent
{
  public Product Product { get; }

  public ProductCreatedEvent(Product product)
  {
    Product = product;
  }
}

Processor:
public class EmailCustomerOnProductCreatedHandler : IDomainEventHandler<ProductCreatedEvent>
{
  private readonly ILogger _logger;

  public EmailCustomerOnProductCreatedHandler(ILoggerFactory loggerFactory)
  {
    _logger = loggerFactory.Create(GetType());
  }

  public void Handle(ProductCreatedEvent @event)
  {
    _logger.Trace($"Send email to customer: Product \"{@event.Product.ProductName}\" was created.");
  }
}

For tests, I inject into the ILoggerFactory handler, but in reality, in addition to ILoggerFactory, many components will be injected there, for example, IEmailSender, etc.
There is also a dispatcher for Autofac:
public class AutofacDomainEventDispatcher : IDomainEventDispatcher
{
  private readonly ILifetimeScope _container;

  public AutofacDomainEventDispatcher(ILifetimeScope container)
  {
    if (container == null)
      throw new ArgumentNullException("container");
    _container = container;
  }
  
  public void Dispatch<TDomainEvent>(TDomainEvent @event) where TDomainEvent : IDomainEvent
  {
    // Для тестов я пока просто пытаюсь получить КОНЕКРЕТНЫЙ обработчик
    var handler = _container.Resolve<IDomainEventHandler<ProductCreatedEvent>>();

    handler.Handle(@event as ProductCreatedEvent); // Привожу к ProductCreatedEvent только для теста
  }
}

Then the whole problem is in setting up Autofac'a:
public static class Bootstrapper
{
  public static void Setup()
  {
    var builder = new ContainerBuilder();
    
    // Регистрируем фабрику для логгеров
    builder.RegisterType(typeof(NLoggerFactory)).As(typeof(ILoggerFactory)).InstancePerRequest();

    // Регистрируем конкретный обработчик
    builder
      .RegisterType(typeof(EmailCustomerOnProductCreatedHandler))
      .As(typeof(IDomainEventHandler<ProductCreatedEvent>))
      .InstancePerLifetimeScope();

    builder.RegisterControllers(typeof(MvcApplication).Assembly);

    var container = builder.Build();
    DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    
    // Setup DomainEventDispatcher
    DomainEventDispatcherProvider.Setup(new AutofacDomainEventDispatcher(container));
  }
}

With this configuration, Autofac throws 2 exceptions in the Dispatch method:
var handler = _container.Resolve<IDomainEventHandler<ProductCreatedEvent>>();

one)
DependencyResolutionException: Unable to resolve the type 'ElibraryEngine.CrossCutting.Logging.NLog.NLoggerFactory' because the lifetime scope it belongs in can't be located. The following services are exposed by this registration:
- ElibraryEngine.CrossCutting.Logging.ILoggerFactory

2)
If you see this during execution of a web application, it generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario). Under the web integration always request dependencies from the dependency resolver or the request lifetime scope, never from the container itself.

I tried to register a handler, not .InstancePerLifetimeScope(), but .InstancePerRequest(), then the exception is like this:
DependencyResolutionException: No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.

I got acquainted with IoC containers recently, so there is a misunderstanding in the use of InstancePerLifetimeScope / InstancePerRequest.
I ask for help in setting up the container.
I would be grateful for any hints!

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question