simpleinjector / SimpleInjector

An easy, flexible, and fast Dependency Injection library that promotes best practice to steer developers towards the pit of success.
https://simpleinjector.org
MIT License
1.22k stars 152 forks source link

.NET 6 Simple Injector has error of the call is ambiguous between SimpleInjectorGenericHostExtensions and SimpleInjectorUseOptionsAspNetCoreExtensions #933

Closed funkel1989 closed 2 years ago

funkel1989 commented 3 years ago

Started a new .NET 6 web API project today and when implementing Simple Injector similar to a way that is worked in a previous .NET 5 web api project i received the following error when calling app.UseSimpleInjector(container);

Error:

Error CS0121 The call is ambiguous between the following methods or properties: 'SimpleInjector.SimpleInjectorGenericHostExtensions.UseSimpleInjector(Microsoft.Extensions.Hosting.IHost, SimpleInjector.Container)' and 'SimpleInjector.SimpleInjectorUseOptionsAspNetCoreExtensions.UseSimpleInjector(Microsoft.AspNetCore.Builder.IApplicationBuilder, SimpleInjector.Container)'

Project Dependencies:

  <ItemGroup>
    <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
    <PackageReference Include="EFCore.NamingConventions" Version="6.0.0" />
    <PackageReference Include="FluentValidation.AspNetCore" Version="10.3.4" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
    <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.14.0" />
    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.0" />
    <PackageReference Include="RockLib.Logging" Version="3.0.8" />
    <PackageReference Include="RockLib.Logging.AspNetCore" Version="3.2.4" />
    <PackageReference Include="SimpleInjector.Integration.AspNetCore.Mvc.Core" Version="5.3.0" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
  </ItemGroup>

I've tried using the SimpleInjector Package without the Mvc.Core integration but that doesn't seem to work. I'm unsure where the ambiguity is coming from. Any help would be appreciated.

Program.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using SimpleInjector;
using SimpleInjector.Lifestyles;
using Container = SimpleInjector.Container;

var builder = WebApplication.CreateBuilder(args);

IConfiguration configuration = builder.Configuration;

Container container = new Container();
container.Options.DefaultLifestyle = Lifestyle.Scoped;
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();

builder.Services.AddMvc();
builder.Services.AddHealthChecks();

builder.Services.AddSimpleInjector(container, options =>
{
    options.AddAspNetCore().AddControllerActivation();
    options.AddLogging();
});

builder.Services.BaseConfiguration(configuration)
    .AddVersioningConfiguration()
    .InitializeContainer(configuration, container)
    .ConfigureModelBindingExceptionHandling(container)
    .AddSwaggerDocumentation()
    .AddCustomDbConfiguration(configuration);

var app = builder.Build();

var apiVersionDescriptionProvider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
app.UseSimpleInjector(container);  // <<< This is what is ambiguious
funkel1989 commented 3 years ago

So it looks like in .NET 6 builder.Build() Returns a Type of WebApplication instead of IApplicationBuilder. WebApplication must contain other extension methods that conflict with SimpleInjector?

I fixed this by abstraction app.UseCors into an extension method that returned IApplicationBuilder and than chained UseSimpleIngector(Container); to the end of that. Still working through other issues before I can determine if this works as a workaround but it compiles. See below for code.

UseCustomCors:

public static IApplicationBuilder UseCustomCors(this IApplicationBuilder app)
    {
        app.UseCors(Constants.CorPolicyName);

        return app;
    }

Program.cs

var app = builder.Build();

var apiVersionDescriptionProvider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
app.UseCustomCors().UseSimpleInjector(container);

One thing to note, if you do IApplicationBuilder app = builder.Build(); this will work for everything still but removes the ability to do the app.Services.GetRequiredService that I need to do using this new .NET 6 Pattern.

funkel1989 commented 3 years ago

Can confirm. The above resolves the problem and everything seems to function as expected but you should consider this issue as a bug IMO as this isn't a workaround someone should have to do.

dotnetjunkie commented 2 years ago

Change this line:

app.UseSimpleInjector(container);

to the following:

app.Services.UseSimpleInjector(container);

I reflected this in the documentation to allow the documentation to work with all versions of ASP.NET Core.


Let me explain why the call to app.UseSimpleInjector(container) causes a compile error.

The new Build() method of the WebApplicationBuilder class has a return type of WebApplication. This WebApplication is new in .NET 6, and it implements both IHost and IApplicationBuilder. This is unfortunate, because the Simple Injector integration packages contain helpful extension methods named UseSimpleInjector for both IHost and IApplicationBuilder. Because WebApplication implements both interfaces, it causes the C# compiler to show the "ambiguous call" (CS0121) compilation error. The compiler simply can't decide which extension method to pick (although in practice it wouldn't make a difference which method it would pick in our case).

The problem could be solved by adding -yet another- extension method, but now directly on WebApplication. Because WebApplication is new in .NET 6, however, it would cause the Simple Injector integration packages to no longer work under earlier releases, which is not an option. That's why a suggest using the solution above, and this is why I updated the documentation to reflect this.

dotnetjunkie commented 2 years ago

The new project template for ASP.NET Core 6 contains a 'simplified' bootstrapper where Program and Startup class are merged into a single file. That single file contains no namespaces, no class or method bodies.

Below is an example that shows how to integrate Simple Injector in to a ASP.NET Core 6 MVC application, that uses this new template This code is the exact same integration as the example in the documentation:

// Program.cs
// Used NuGet Packages: SimpleInjector + SimpleInjector.Integration.AspNetCore.Mvc
using SimpleInjector;

var container = new Container();

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
var services = builder.Services;

services.AddControllersWithViews();

services.AddLogging();
services.AddLocalization(options => options.ResourcesPath = "Resources");

services.AddSimpleInjector(container, options =>
{
    options.AddAspNetCore()
        .AddControllerActivation()
        .AddViewComponentActivation()
        .AddPageModelActivation()
        .AddTagHelperActivation();

    options.AddLogging();
    options.AddLocalization();
});

InitializeContainer();

void InitializeContainer()
{
    container.Register<IUserService, UserService>(Lifestyle.Singleton);
}

WebApplication app = builder.Build();

app.Services.UseSimpleInjector(container);

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.UseMiddleware<CustomMiddleware1>(container);
app.UseMiddleware<CustomMiddleware2>(container);

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

container.Verify();

app.Run();
funkel1989 commented 2 years ago

Thanks a lot for the information this post provides. This works great!