Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Options initializers appear to run out of order in ASP.NET 5 #695

Closed
sklose opened this issue Nov 17, 2015 · 4 comments
Closed

Options initializers appear to run out of order in ASP.NET 5 #695

sklose opened this issue Nov 17, 2015 · 4 comments

Comments

@sklose
Copy link

sklose commented Nov 17, 2015

I am trying to use Autofac as my DI container in a new ASP.NET 5 MVC project. It works fine with the simple example codes I tried, but once I start customizing some MVC options the application stops working when Autofac is used.

For instance, this code (taken from an sample) works fine:

using System;
using Microsoft.AspNet.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Autofac;
using Autofac.Extensions.DependencyInjection;

namespace HelloMvc
{
    public class Startup
    {
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            var containerBuilder = new ContainerBuilder();
            containerBuilder.Populate(services);
            var container = containerBuilder.Build();

            return container.Resolve<IServiceProvider>();
        }

        public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();            
            app.UseDeveloperExceptionPage();
            app.UseMvcWithDefaultRoute();
            app.UseWelcomePage();
        }
    }
}

Then I add a few lines to configure the Razor Engine and this blows up with a NullReferenceException because options.FileProvider is NULL

using System;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.FileProviders;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Autofac;
using Autofac.Extensions.DependencyInjection;

namespace HelloMvc
{
    public class Startup
    {
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.Configure<RazorViewEngineOptions>(options =>
            {
                var currentProvider = (PhysicalFileProvider) options.FileProvider;
                System.Console.WriteLine(currentProvider.Root);
            });

            var containerBuilder = new ContainerBuilder();
            containerBuilder.Populate(services);
            var container = containerBuilder.Build();

            return container.Resolve<IServiceProvider>();
        }

        public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();            
            app.UseDeveloperExceptionPage();
            app.UseMvcWithDefaultRoute();
            app.UseWelcomePage();
        }
    }
}

Then I change it back to use the default ASP.NET DI container, and it works fine (options.FileProvider is set to an instance of PhysicalFileProvider).

using System;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.FileProviders;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace HelloMvc
{
    public class Startup
    {
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.Configure<RazorViewEngineOptions>(options =>
            {
                var currentProvider = (PhysicalFileProvider) options.FileProvider;
                System.Console.WriteLine(currentProvider.Root);
            });

            return services.BuildServiceProvider();
        }

        public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();            
            app.UseDeveloperExceptionPage();
            app.UseMvcWithDefaultRoute();
            app.UseWelcomePage();
        }
    }
}

Any idea why the FileProvider fails to initialize when using Autofac?

@tillig
Copy link
Member

tillig commented Nov 17, 2015

Using beta 8 with this code, I'm not able to reproduce the issue. I see the FileProvider as being set.

However, I can surmise that if you are seeing it, it is most likely an ordering difference in the way options setups are evaluated across containers.

The following is what I think is happening to cause what you're seeing (since I can't repro it).

The way options work, when you register a setup delegate it adds a little service to the container that wraps the delegate. (There's more on this in #659 and it's a long story, so I won't go into it all here - if you're interested in how options work, check out the aspnet/Options repo.)

When you AddMvc() it adds a setup method to the services collection that initializes the FileProvider. Running services.Configure<T> adds a second setup method registration to the service collection. If you were to look at just those registrations in the services collection, it would look roughly looks like this (in pseudocode)

var razorSetupOptions = new [] {
  theDefaultInitializer,
  yourCustomInitializer
};

I'm betting when the default dependency resolver is resolving the list of options configuration delegates that it's resolving in that exact order so it runs the default initializer first - thus it's populated by the time it runs your initializer. On the other hand, Autofac may be resolving the list in the opposite order (last in first out) so yours is running before the default initializer gets a chance to run.

Something you can try is to move your services.ConfigureOptions<T> call above services.AddMvc() to see if it makes a difference.

I'm not sure there's any specified guarantee in the options mechanism that options initializers will run in a specific order. I probably wouldn't make the assumption that things are going to happen in a specified order - sort of the way event handlers aren't guaranteed to run in a specified order.

But, like I said, I wasn't able to reproduce it. I added this code to the sample app in the Autofac codeline and the FileProvider was set, so it's just a guess.

@tillig tillig closed this as completed Nov 17, 2015
@tillig tillig changed the title Autofac fails to resolve dependencies in ASP.NET 5 Options initializers appear to run out of order in ASP.NET 5 Nov 17, 2015
@sklose
Copy link
Author

sklose commented Nov 17, 2015

Thanks, moving services.AddMvc() after the Configure call did the trick. I am wondering though if the Autofac behavior might cause other issues down the line where settings get applied in the wrong order and the final value is wrong.

If you want to reproduce the issue: for me it happens on Clr-x86-1.0.0-rc2-16128 with Autofac rc1-171 and I copied these files into my project https://github.com/autofac/Autofac/tree/develop/src/Autofac.Extensions.DependencyInjection cause the nuget package didn't work for me with the latest rc2.

@tillig
Copy link
Member

tillig commented Nov 17, 2015

Given what I mentioned is, and I stress this, a guess since I couldn't repro it at the time I can't say whether the final release will still have the described issue or not. When I tested your code it was with the beta8 release of MVC and Autofac - the supported stuff released to NuGet.

We're still working on RC1 compatibility for Autofac so RC2 is definitely a bit past the bleeding edge. A lot changes between releases from the aspnet team so if you're going to live on the edge... you may see things that don't work right.

@dazinator
Copy link

I just hit this issue, thank you @tillig that worked for me also.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants