Search code examples
entity-frameworkdependency-injectioninversion-of-controlautofac

How to resolve Autofac per-request service from custom attribute


I have configured my EF context configured like so

        b.RegisterAssemblyTypes(webAssembly, coreAssembly)
            .Where(t => t.IsAssignableTo<DbContext>())
            .InstancePerLifetimeScope();

I would like to use it from a custom authorization attribute that hits the database using my EF context. This means no constructor-injection. I achieve this by using CommonSeviceLocator

        var csl = new AutofacServiceLocator(container);
        ServiceLocator.SetLocatorProvider(() => csl);

...

        var user = await ServiceLocator.Current
           .GetInstance<SiteContext>().FindAsync(id);

I am finding that this fails with a "multiple connections not supported" error if the browser issues two simultaneous requests to routes using this attribute. It seems like this might be due to what is mentioned in this answer. My guess is that ServiceLocator resolves from the root scope rather than the web request scope and the two request are conflicting (either request in isolation works fine).

This seems confirmed by that when I change to InstancePerRequest() I get this from any invocation of the attribute.

 Autofac.Core.DependencyResolutionException No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This 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 DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container  itself.

So it seems like maybe ServiceLocator is simply not the way to go.

How do I resolve the request-scoped SiteContext from inside the attribute (using a service-locator pattern)?


Solution

  • Your issue derives from the fact that you are trying to put behavior inside of an Attribute. Attributes are for defining meta-data on code elements and assemblies, not for behavior.

    Microsoft's marketing of Action Filter Attributes has led people implementing DI down the wrong path by putting both the Filter and the Attribute into the same class. As described in the post passive attributes, the solution is to break apart filter attributes into 2 classes:

    1. An attribute that contains no behavior to mark code elements with meta-data.
    2. A globally-registered filter that scans for the attribute and executes the desired behavior if present.

    See the following for more examples:

    Another option is to use IFilterProvider to resolve the filters as in IFilterProvider and separation of concerns.

    Once you get your head around the fact that Attributes should not be doing anything themselves, using them with DI is rather straightforward.