3
votes

What should be the Autofac 3.5 configuration for Asp.net Mvc 5.2, SignalR 2.1, MS Owin (Katana) 3.0? Is there less complex way to register Autofac resolvers (there is two of them now)? Or why ILifetimeScope is not visible for my hub?

The exception:

Autofac.Core.DependencyResolutionException: An exception was thrown while invoking the constructor 'Void .ctor(Autofac.ILifetimeScope)' on type 'OperatorHub'. --->

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. (See inner exception for details.) --->

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.

In my OwinStartup (see autofac + mvc owin and autofac + signalr in owin):

public void Configuration(IAppBuilder app)
{
    var builder = new ContainerBuilder();

    // ... registration. There is .InstancePerRequest() and .SingleInstance()

    Autofac.Integration.Mvc.RegistrationExtensions.RegisterControllers(builder,typeof(MvcApplication).Assembly);
    Autofac.Integration.SignalR.RegistrationExtensions.RegisterHubs(builder, Assembly.GetExecutingAssembly());

    var container = builder.Build();

    // 1st resolver
    DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

    app.UseAutofacMiddleware(container);
    app.UseAutofacMvc();

    // yet the 2nd resolver!
    app.MapSignalR(new HubConfiguration { Resolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(container) });
}

The hub:

public class OperatorHub : Hub
{
    public OperatorHub(ILifetimeScope hubLifetimeScope) 
    {
        hubLifetimeScope.BeginLifetimeScope(); 
        // ...

        // HERE IT FALLS. The IMyService relates to MyDbContext (see below)
        var myservice = hubLifetimeScope.Resolve<IMyService>();

    }
}

UPDATE

The breaking component registration (EF Context:

        builder.RegisterType<MyDbContext>().AsSelf().As<DbContext>().InstancePerRequest("OwinLifetimeScope");

In short the bug is the MyDbContext is not in the 'root' lifetime scope which is passed to OperatorHub constructor.

UPDATE 2

The solution with the help of @TravisIllig is to register the MyDbContext service using .InstancePerLifetimeScope() and to create the one in the hub. Another lifetime scope would be created for http request in asp mvc. Create help at Sharing Dependencies Across Apps Without Requests.

Also the hub should not dispose the given scope as it is the root one which results in ObjectDisposedException on the second run.

1

1 Answers

7
votes

There is an FAQ on handling this exact exception on the Autofac doc site. The problem stems from the fact you're using InstancePerRequest in conjunction with SignalR, which, also per the documentation:

Due to SignalR internals, there is no support in SignalR for per-request lifetime dependencies.

You do appear to have looked at the Autofac SignalR docs as I see you've injected a lifetime scope to help you manage instance lifetimes, but that doesn't give you per-request lifetime scopes, it just gives you a hub lifetime scope. I might suggest revisiting that doc for a refresher.

The FAQ I mentioned, in conjunction with the SignalR integration docs, should point you to the right solution for your app. Many people simply switch their registrations from InstancePerRequest to InstancePerLifetimeScope but I strongly encourage you to read the FAQ and check out your options before just jumping to that decision. It may be the right choice, but it may not be - it depends on how your app works internally.