SimpleInjector `Controller 注册为瞬态,但实现了 ApiController 的 IDisposable` 错误

SimpleInjector `Controller is registered as transient, but implements IDisposable` Error for ApiController

因此,正如我在研究中看到的那样,这个错误多年来一直给其他用户带来麻烦。 他们的建议解决方案另一方面,我的项目不起作用 不知何故。

所以,我的创业公司 class 是;

public class Startup
{
    private Container container = new Container();
    public IConfigurationRoot Configuration { get; private set; }

    public void ConfigureServices(IServiceCollection services)
    {
        container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
        var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
        this.Configuration = new ConfigurationBuilder().SetBasePath(
            Directory.GetCurrentDirectory()).AddJsonFile($"appsettings.{environmentName}.json").Build();
        services.AddCors(options =>
        {
            options.AddPolicy(
              "CorsPolicy",
              builder => builder.WithOrigins(new[] { "http://localhost" })
              .AllowAnyMethod()
              .AllowAnyHeader()
              .AllowCredentials()
            );
        });

        services.AddControllers(); // ERR 1

        services.AddSimpleInjector(container, options => /// ERR 2
        {
            options
                .AddAspNetCore()
                .AddControllerActivation();
        });
        services.AddCors();

        InitializeContainer();
    }

    private void InitializeContainer()
    {
        // Register services
        ConnectionConfig conf = new ConnectionConfig();
        AppSettings settings = new AppSettings();
        PathConfig path = new PathConfig();
        Configuration.GetSection("AppSettings").Bind(settings);
        Configuration.GetSection("ConnectionConfig").Bind(conf);
        Configuration.GetSection("PathConfig").Bind(path);
        container.RegisterInstance<ConnectionConfig>(conf);
        container.RegisterInstance<PathConfig>(path);
        container.RegisterInstance<AppSettings>(settings);

        new CoreModule().RegisterServices(container);
        new DataModule().RegisterServices(container);
        new ServiceModule().RegisterServices(container);
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseDeveloperExceptionPage();
        app.UseCors(
           options => options
           .AllowAnyOrigin()
           .AllowAnyMethod()
           .AllowAnyHeader()
        );

        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });

        app.UseSimpleInjector(container);

        container.Verify(); /// ERR 3
    }
}

ERR 3 的错误是;

建议的解决方案说,我应该使用 Scoped 的 LifestyleScope 注册这些控制器,但是当我尝试这样做时,它在 ERR 2 处出现错误。 ERR 2:

A registration for the Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager is missing from the ASP.NET Core configuration system. This is most likely caused by a missing call to services.AddMvcCore() or services.AddMvc() as part of the ConfigureServices(IServiceCollection) method of the Startup class. A call to one of those methods will ensure the registration of the ApplicationPartManager.

at SimpleInjector.SimpleInjectorAspNetCoreBuilderMvcCoreExtensions.GetApplicationPartManager(IServiceCollection services, String methodName)
at SimpleInjector.SimpleInjectorAspNetCoreBuilderMvcCoreExtensions.AddControllerActivation(SimpleInjectorAspNetCoreBuilder builder)
at ....Startup.<>c.<ConfigureServices>b__5_1(SimpleInjectorAddOptions options) in C:\Projects\...\Startup.cs:line 48
at SimpleInjector.SimpleInjectorServiceCollectionExtensions.AddSimpleInjector(IServiceCollection services, Container container, Action`1 setupAction)
at ....Startup.ConfigureServices(IServiceCollection services) in C:\Projects\...\Startup.cs:line 46
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.InvokeCore(Object instance, IServiceCollection services)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass9_0.<Invoke>g__Startup|0(IServiceCollection serviceCollection)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.Invoke(Object instance, IServiceCollection services)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass8_0.<Build>b__0(IServiceCollection services)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.UseStartup(Type startupType, HostBuilderContext context, IServiceCollection services)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass12_0.<UseStartup>b__0(HostBuilderContext context, IServiceCollection services)
at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
at Microsoft.Extensions.Hosting.HostBuilder.Build()
at ....Program.Main(String[] args) in C:\Projects\...\Program.cs:line 16

我的问题的根源是,我从 BaseApiController 返回 IHttpAction 的意愿不佳。我这样做的原因是,要确保我的 API 的客户在发生错误时始终了解正在发生的事情。如果不返回 InternalServerErrorOk() 等,我就很难向客户解释我的错误。

这是BaseApiController:

// When I use `ControllerBase` here for the inheritance, API works like a charm 
// after getting rid of the `IHttpActionResult` and writing `T` instead.

public class BaseApiController : ApiController 
{
    private readonly LogModule _logModule;

    public BaseApiController(LogModule logModule)
    {
        _logModule = logModule;
    }

    public IHttpActionResult ExecuteAction<T>(Func<BaseResponse<T>> func)
    {
        try
        {
            return Ok(func());
        }
        catch (Exception ex)
        {
            var requestScope = Request.GetDependencyScope();
            var logModule = requestScope.GetService(typeof(LogModule)) as LogModule;

            StackTrace stackTrace = new StackTrace();
            var description = "API." + this.GetType().Name + "." + stackTrace.GetFrame(1).GetMethod().Name;
            logModule.Error(description, ex);
            return InternalServerError();
        }
    }
}

这是我使用的控制器;

[Route("api/foo")]
[ApiController]
public class FooController : BaseApiController
{
    private readonly FooService _fooService;

    public FooController(FooService fooService, LogModule logModule) : base(logModule)
    {
        _fooService = fooService;
    }

    [HttpPost]
    [Route("create_foo")]
    public System.Web.Http.IHttpActionResult GetFoo([FromBody] FooRequest request)
    {
        var baseResponse = ExecuteAction(() =>
        {
            return _fooService.AddFoo(request);
        });
        return baseResponse;
    }
}

总而言之,如何将 SimpleInjector 与 ApiController 一起使用?

要将您的控制器注册为 Scoped,您可以使用以下代码:

// inside ConfigureServices or Startup ctor
this.container.Options.LifestyleSelectionBehavior = new ApiControllersAsScopedBehavior();

// behavior
internal class ApiControllersAsScopedBehavior : ILifestyleSelectionBehavior
{
    public Lifestyle SelectLifestyle(Type implementationType) =>
        typeof(ApiController).IsAssignableFrom(implementationType)
            ? Lifestyle.Scoped
            : Lifestyle.Transient;
}