LightInject icon indicating copy to clipboard operation
LightInject copied to clipboard

StackOverflowException with .NET Core 2.2 in AddMvc()

Open ytzhakov opened this issue 6 years ago • 14 comments

Just upgraded my ASP.NET Core MVC project from 2.1 to 2.2

LightInject throws StackOverflowException when calling app.UseMvc() or app.UseMvcWithDefaultRoute() I was able to get rid of the issue by setting the Compatibility Version to 2.1.

Doesnt work:

services.AddMvc().AddControllersAsServices().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); app.UseMvcWithDefaultRoute(); //throws

Works:

services.AddMvc().AddControllersAsServices().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); app.UseMvcWithDefaultRoute(); //all good

Any ideas?

ytzhakov avatar Dec 06 '18 19:12 ytzhakov

Could you also post your startup.cs and program.cs?

seesharper avatar Dec 06 '18 20:12 seesharper

Unable to reproduce so far.

Here is my startup.cs

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            services.AddMvc().AddControllersAsServices().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseMvcWithDefaultRoute();            
        }
    }

and my program.cs

 public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseLightInject()
                .UseStartup<Startup>();
    }

seesharper avatar Dec 06 '18 20:12 seesharper

public class Program { public static void Main(string[] args) { WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .Build() .Run(); } }

` public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; }

    public IConfiguration Configuration { get; }

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddMvc()
                .AddControllersAsServices()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

        services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy",
                                  builder => builder.AllowAnyOrigin()
                                                    .AllowAnyMethod()
                                                    .AllowAnyHeader()
                                                    .AllowCredentials());
            });


        var containerOptions = new ContainerOptions { EnablePropertyInjection = false };
        var container = new ServiceContainer(containerOptions);
        
        IServiceProvider serviceProvider = container.CreateServiceProvider(services);

        return serviceProvider;
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDatabaseErrorPage();
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseMvcWithDefaultRoute();
        app.UseCors("CorsPolicy");
    }
}`

Maybe I should you .UseLightInject() in the program instead of what I use now from LightInject.Microsoft.DependencyInjection ?

ytzhakov avatar Dec 06 '18 21:12 ytzhakov

Yeah, that would be better indeed. You find that if you pull in the LightInject.Microsoft.AspnetCore.Hosting package

seesharper avatar Dec 06 '18 21:12 seesharper

image

UseLightInject() did not seem to replace the default service provider

ytzhakov avatar Dec 06 '18 21:12 ytzhakov

That is the container that is used to inject services into the startup class. https://docs.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-2.2 If I remember correctly they operate with two containers. One for app services used during startup and one for the rest of the application.

seesharper avatar Dec 06 '18 21:12 seesharper

well I might be missing something really stupid but it doesn't seem to be the case. I've created a new ASP.NET Core 2.2 project, used UseLightInject() Then I requested IServiceProvider in ValuesController constructor and I'm still getting microsofts container. Here's the code:

`

    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                   .UseLightInject()
                   .UseStartup<Startup>();

`

`

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc()
                .AddControllersAsServices()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

        services.AddTransient<IServiceProvider>(sp => sp);

    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseMvc();
    }

`

And here's what I get in ValuesController:

image

If I add the following code to startup.cs, lightinject does seem to assume control, but it still throws exception when compitability is set to 2.2:

`

    public void ConfigureContainer(IServiceContainer container)
    {
        
    }

`

Could you please add it to your startup.cs and see if it works for you (and you can get the actually lightinject service provider in your controller)?

thanks.

ytzhakov avatar Dec 07 '18 11:12 ytzhakov

Hi again @ytzhakov and thanks for putting so much time into this.

There is a lot of more or less well documented conventions in aspnet core.

It seems like you need this in your startup to make it replace the service provider

 public void ConfigureContainer(IServiceContainer container)
        {
           
        }

But then we are back to a StackoverflowException when compatibility is set to 2_2.

Looking into it

seesharper avatar Dec 07 '18 13:12 seesharper

This is the service it stack overflows on

Microsoft.Extensions.Options.IOptions`1[Microsoft.AspNetCore.Routing.EndpointOptions]
Microsoft.Extensions.Options.IOptions`1[Microsoft.AspNetCore.Routing.EndpointOptions]
Microsoft.Extensions.Options.IOptions`1[Microsoft.AspNetCore.Routing.EndpointOptions]
Microsoft.Extensions.Options.IOptions`1[Microsoft.AspNetCore.Routing.EndpointOptions]
Microsoft.Extensions.Options.IOptions`1[Microsoft.AspNetCore.Routing.EndpointOptions]
Microsoft.Extensions.Options.IOptions`1[Microsoft.AspNetCore.Routing.EndpointOptions]
Microsoft.Extensions.Options.IOptions`1[Microsoft.AspNetCore.Routing.EndpointOptions]
Microsoft.Extensions.Options.IOptions`1[Microsoft.AspNetCore.Routing.EndpointOptions]
Microsoft.Extensions.Options.IOptions`1[Microsoft.AspNetCore.Routing.EndpointOptions]
Microsoft.Extensions.Options.IOptions`1[Microsoft.AspNetCore.Routing.EndpointOptions]
Microsoft.Extensions.Options.IOptions`1[Microsoft.AspNetCore.Routing.EndpointOptions]
Microsoft.Extensions.Options.IOptions`1[Microsoft.AspNetCore.Routing.EndpointOptions]

seesharper avatar Dec 07 '18 13:12 seesharper

Looks like this might be the class in question.

Note: Just putting down my findings as I am debugging this

https://github.com/aspnet/Extensions/blob/release/2.2/src/Options/Options/src/OptionsFactory.cs

seesharper avatar Dec 07 '18 15:12 seesharper

@ytzhakov I found it. It is related to IEnumerable<T> and variance.

Variance is on by default in LightInject. It means that GetAllInstances<Foo> will bring back all instances of Foo and also DerivedFoo. The problem here is that some IEnumerable<T> somewhere deep in AspNetCore is resolved bringing back something that causes a StackOverFlowEx eption. You can turn this behaviour off by doing containerOption.EnableVariance=false. I need to talk to the AspNetCore team before I change the default here. I have a feeling it was not intentional to require this behaviour.

seesharper avatar Dec 07 '18 20:12 seesharper

Great.. it would really be interesting to find out whether or not this was intentional and if so, why... Please update this issue when you have more information

ytzhakov avatar Dec 08 '18 01:12 ytzhakov

https://github.com/aspnet/AspNetCore/issues/4531

seesharper avatar Dec 08 '18 11:12 seesharper

I have a similar problem due to dependency injection. Described my decision here https://github.com/seesharper/LightInject/issues/473

AlexandrSitdikov avatar Jan 29 '19 06:01 AlexandrSitdikov