1

I've setup a series of tasks using the Application events in a global.asax.cs file like so:

// Note: For instructions on enabling IIS7 classic mode, 
// visit http://go.microsoft.com/?LinkId=301868
public class MvcApplication : System.Web.HttpApplication
{
private static IContainer ContainerGlobal;
private static ILogger Logger;

public ILifetimeScope Container
{
    get { return (ILifetimeScope)HttpContext.Current.Items["_Container"]; }
    set { HttpContext.Current.Items["_Container"] = value; }
}

protected void Application_Start()
{
    AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;

    AreaRegistration.RegisterAllAreas();
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    //configure the Autofac IoC container
    var container = AutofacBuilder.Configure(Assembly.GetExecutingAssembly(),
        new MvcModule(), new TaskModule());

    // startup the logging
    var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase)
        .Replace("file:\\", string.Empty);
    var file = new FileInfo(path + @"\log4net.ui.config");
    Logger = new Logger(MethodBase.GetCurrentMethod().DeclaringType, file);

    var runAtInits = container.Resolve<IEnumerable<IRunAtInit>>();

    if (runAtInits != null)
    {
        Logger.LogFormat(LogType.Debug, "Found {0} IRunAtInit instances", 
            runAtInits.Count());

        foreach (var task in runAtInits)
        {
            task.Execute();
        }
    }

    var runAtStartups = container.Resolve<IEnumerable<IRunAtStartup>>();

    if (runAtStartups != null)
    {
        Logger.LogFormat(LogType.Debug, "Found {0} IRunAtStartup instances", 
            runAtStartups.Count());

        foreach (var task in runAtStartups)
        {
            task.Execute();
        }
    }

    ContainerGlobal = container;
}

public void Application_BeginRequest()
{
    try
    {
        Container = ContainerGlobal.BeginLifetimeScope();

        var runOnEachRequests = 
            Container.Resolve<IEnumerable<IRunOnEachRequest>>();

        if (runOnEachRequests == null)
            return;

        Logger.LogFormat(LogType.Debug, "Found {0} IRunOnEachRequest instances", 
            runOnEachRequests.Count());

        foreach (var task in runOnEachRequests)
        {
            task.Execute();
        }
    }
    catch(Exception ex)
    {
        Logger.Log(LogType.Error, ex);
    }
}

public void Application_Error()
{
    try
    {
        var runOnErrors = Container.Resolve<IEnumerable<IRunOnError>>();

        if (runOnErrors == null)
            return;

        Logger.LogFormat(LogType.Debug, "Found {0} IRunOnError instances", 
            runOnErrors.Count());

        foreach (var task in runOnErrors)
        {
            task.Execute();
        }
    }
    catch (Exception ex)
    {
        Logger.Log(LogType.Error, ex);
    }
}

public void Application_EndRequest()
{
    try
    {
        var runAfterEachRequests = 
            Container.Resolve<IEnumerable<IRunAfterEachRequest>>();

        if (runAfterEachRequests == null)
            return;

        Logger.LogFormat(LogType.Debug, "Found {0} IRunAfterEachRequest instances",
            runAfterEachRequests.Count());

        foreach (var task in runAfterEachRequests)
        {
            task.Execute();
        }
    }
    catch (Exception ex)
    {
        Logger.Log(LogType.Error, ex);
    }
    finally
    {
        if (Container != null)
        {
            Container.Dispose();
            Container = null;
        }
    }
}
}

This is discussed by Matt Honeycut here:

https://github.com/MattHoneycutt/Fail-Tracker

As we make extensive use of Autofac throughout our application, we've implemented it using Autofac rather than Structuremap.

The problem is, we get exceptions raised for the following events: Application_EndRequest and sometimes for Application_BeginRequest. The exception raised in both cases is:

"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"

We've ensured that we use the ILifeScope for our Container object, rather than the root Container object. However, this doesn't fix the errors. Does anyone else have any suggestions as to what else we need to do?

For information, the call to AutofacBuilder.Configure above registers all necessary library types and modules and returns the root Container from autofac by calling the Build method.

thanks

PS We're using: Visual Studio 2013, MVC 5.1 Autofac 3.3

Steven
  • 166,672
  • 24
  • 332
  • 435
ossentoo
  • 1,675
  • 2
  • 20
  • 44
  • Great, thanks for that insight Travis. Really appreciate your pointing me in the right direction. Wasn't aware of the requirement for HttpContext in some of my registrations. – ossentoo Apr 30 '14 at 08:32

1 Answers1

1

The error message pretty much says it all, but to be more specific, one or more of the following is occurring:

  • One or more of your begin/end request handlers is being registered as InstancePerHttpRequest
  • One or more of the dependencies required by your begin/end request handlers is being registered as InstancePerHttpRequest
  • One or more of the request handlers (or the dependencies for the request handlers) is trying to use DependencyResolver.Current during app start/end.

The Autofac dependency resolver requires a web request context to work. You can read more about that in the answer here: Autofac - The request lifetime scope cannot be created because the HttpContext is not available - due to async code?

App start/end don't have a web request running. They're outside the request pipeline.

Go through your registrations (which appear to be in AutofacBuilder.Configure) and check to see which ones are InstancePerHttpRequest. Something in there that is required during your handlers' execution is getting incorrectly registered that way, so when the web request scope isn't found - boom. And, again, it may not be just one thing - it could be that all your handlers are registered correctly, but one of the dependencies for your handlers are registered InstancePerHttpRequest.

If you find the issues and you don't want to switch to registering them as SingleInstance, consider switching the registration to InstancePerLifetimeScope. Chances are, unless your app is creating a bunch of lifetime scopes for units of work or something, that InstancePerLifetimeScope will behave just like InstancePerHttpRequest but will properly resolve without a web request.

That said, I would recommend wrapping the task execution in a lifetime scope so memory gets cleaned up:

using(var scope = container.BeginLifetimeScope())
{
   // Resolve from a scope.
   var runAtStartups = scope.Resolve<IEnumerable<IRunAtStartup>>();
   // Do the run, etc.
}

Finally, something to be aware of: I see you're manually generating some sort of request lifetime scope yourself in the BeginRequest event. I'm not sure if it's important, but the scope you're creating there will not be the request lifetime scope that Autofac actually uses. It will only be used for your components, and it won't work with InstancePerHttpRequest registered stuff.

Community
  • 1
  • 1
Travis Illig
  • 23,195
  • 2
  • 62
  • 85