Search code examples
asp.net-core-webapiautofac

Autofac create 2 instances of service


Autofac resolves as 2 separate instances when I expect one instance per API request.

I see one instance from controller till a command handler and another instance in one of my domain event handlers, the domain handler references the dispatcher object through property injection.

The problem here is I am not seeing the same object instance of UserContext where I inject and modify through and Web API controller in my Domain event handler which invokes through DomainEvent.dispatcher.Raise();.

I follow the same instruction as per Autofac doc. Could anybody shed some light on what I am missing here?

//Auotfac registration  
public class AutofacModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
         builder
            .RegisterType<DomainEventDispatcher>()
            .As<IDomainEventDispatcher>().InstancePerLifetimeScope();

         builder
            .RegisterType<DomainEvent>()
            .As<IDomainEvent>).PropertiesAutowired().InstancePerLifetimeScope().AutoActivate();

         builder
            .RegisterType<UserContext>()
            .As<IApplicationContext>().InstancePerLifetimeScope();
    }
}

//My DomainEvent class
//--------------------------
internal class DomainEvent:IDomainEvent
{
    public static IDomainEventDispatcher dispatcher { get;  set; }
}

Solution

  • This is going to be hard to figure out because you didn't provide a Minimal, Complete, and Verifiable Example. That example and all code related to your question should be in the question - if there are questions asked through comments... update your question with the answers.

    That said, I can see a pitfall that is probably what you're falling into.

    First, I see this:

    builder
      .RegisterType<DomainEventDispatcher>()
      .As<IDomainEventDispatcher>()
      .InstancePerLifetimeScope();
    

    In ASP.NET / ASP.NET Core this will generally equate to "one instance per request," which appears to be the direction you want to go. However, it implies that it's only resolved from request lifetime scopes.

    The root container is a lifetime scope, too. That's where singletons get resolved. It's also where singleton dependencies get resolved. There's a looooot of documentation on lifetime scopes.

    This is important, because I see two other things that raise some concern.

    First...

    internal class DomainEvent : IDomainEvent
    {
        public static IDomainEventDispatcher dispatcher { get;  set; }
    }
    

    The static there is really concerning because you have the dispatcher registered as instance per lifetime scope... but static here is going to be a threading problem. I assume (though can't verify, again, because of the lack of MCVE) you're doing something such that when a request comes in you're setting this static variable. This probably works great in development when there's only one request coming in at a time. This is going to be really bad news when you have two requests and one instance of the dispatcher stomps over the other instance.

    Don't mix static and per-request. Static things are singletons. Always.

    Second...

    builder
      .RegisterType<DomainEvent>()
      .As<IDomainEvent>()
      .PropertiesAutowired()
      .InstancePerLifetimeScope()
      .AutoActivate();
    

    You have what looks to be something that should be created per-request but you also have AutoActivate(). That's going to resolve one of these from the root container at container build time. I'm not sure what's going on there, maybe a shared data connection is getting started up or something, but that means you're getting a DomainEvent resolved from the container at container build time and that instance will be cached until the container is disposed because you specified instance per lifetime scope... and, again, the container is a scope. If you were to ask for a second one of these at the root level (like for the dependency on a singleton) you'd get this root one, not the per-request one.

    I assume later you're also getting DomainEvent objects resolved somewhere. Those are likely coming from the request scope if they're in controllers or something.

    Thus, you have a mixed up set of requirements for scopes and activation.

    • Static reference to IDomainEventDispatcher that will be a singleton for the whole app... but per lifetime scope registration for IDomainEventDispatcher.
    • Auto-activate for IDomainEvent which will happen at the root container... but a desire to have it be per-request due to the per lifetime scope registration.

    There's apparently a UserContext in play here, too, but there's nothing in the code that explains how it's consumed or what it's doing, so that part of the question can't be addressed.

    I would recommend:

    • Switch the static reference to the dispatcher to be an instance property. If you want to make the dispatcher a singleton, register it as SingleInstance with Autofac.
    • Avoid property injection if you can. If the DomainEvent requires a dispatcher, put it in the constructor. That will ensure you always get one. Property injection won't fail if it can't inject a property.
    • Don't use AutoActivate on the per-request items. If you have a shared data connection or something that needs to be fired up at container build time, separate that logic out to some sort of data provider that can be registered as a singleton. This will also avoid the root-level resolution of things that should be per-request.