InvalidOperationException Cannot modify ServiceCollection after application is built .Net6 异常
背景
我用了一个叫Unchase.Swashbuckle.AspNetCore.Extensions
的库来加强Swagger的文档,我一般写法是这样的:
builder.Services.AddSwaggerGen(c =>
{
//文档
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "『xxx』后端接口:myapi",
Version = "v1"
});
c.SwaggerDoc("v2", new OpenApiInfo { Title = "v2", Version = "v2" });
c.MapType<long>(() => new OpenApiSchema { Type = "string" });
//获取注释
var apiXmlPath = Path.Combine(AppContext.BaseDirectory, $"{Assembly.GetExecutingAssembly().GetName().Name}.xml");
c.IncludeXmlComments(apiXmlPath);
c.DescribeAllParametersInCamelCase();
c.AddEnumsWithValuesFixFilters(services, o =>
{
o.IncludeDescriptions = true;
o.IncludeXEnumRemarks = true;
o.DescriptionSource = DescriptionSources.DescriptionAttributesThenXmlComments;
o.IncludeXmlCommentsFrom(apiXmlPath);
});
});
...
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
...
在.net core 3.1 和.net5 都运行好好的,在.net6就报异常:
InvalidOperationException: Cannot modify ServiceCollection after application is built.
System.InvalidOperationException: Cannot modify ServiceCollection after application is built.
at Microsoft.AspNetCore.WebApplicationServiceCollection.CheckServicesAccess()
at Microsoft.AspNetCore.WebApplicationServiceCollection.Add(ServiceDescriptor item)
at Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(IServiceCollection services, Type serviceType, Object implementationInstance)
at Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton[TService](IServiceCollection services, TService implementationInstance)
at Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.Configure[TOptions](IServiceCollection services, String name, Action`1 configureOptions)
at Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.Configure[TOptions](IServiceCollection services, Action`1 configureOptions)
at Unchase.Swashbuckle.AspNetCore.Extensions.Extensions.SwaggerGenOptionsExtensions.AddEnumsWithValuesFixFilters(SwaggerGenOptions swaggerGenOptions, IServiceCollection services, Action`1 configureOptions)
at WebApi6._0.Bootstrapper.<>c__DisplayClass0_0.<AddSwagger>b__0(SwaggerGenOptions c) in D:\learn\netcoreTest\WebApi6.0\Bootstrapper.cs:line 32
at Microsoft.Extensions.Options.ConfigureNamedOptions`1.Configure(String name, TOptions options)
at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
at Microsoft.Extensions.Options.UnnamedOptionsManager`1.get_Value()
at Swashbuckle.AspNetCore.SwaggerGen.ConfigureSwaggerGeneratorOptions..ctor(IOptions`1 swaggerGenOptionsAccessor, IServiceProvider serviceProvider, IWebHostEnvironment hostingEnv)
at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(Type serviceType)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.SwaggerGenServiceCollectionExtensions.<>c.<AddSwaggerGen>b__0_1(IServiceProvider s)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass2_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.GetService(IServiceProvider sp, Type type, Type middleware)
at lambda_method1(Closure , Object , HttpContext , IServiceProvider )
at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass5_1.<UseMiddleware>b__2(HttpContext context)
at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
下了源码下来发现,问题应该出现在这行:
...
c.AddEnumsWithValuesFixFilters(services, o =>
{
o.IncludeDescriptions = true;
o.IncludeXEnumRemarks = true;
o.DescriptionSource = DescriptionSources.DescriptionAttributesThenXmlComments;
o.IncludeXmlCommentsFrom(apiXmlPath);
});
...
对应nuget源码的这行:
if (configureOptions != null)
{
services?.Configure(configureOptions);
}
原因是:.net6之后已经不允许在
var app = builder.Build();
app build后再做 services?.Configure()操作
那简单;
解决
思路是,先在build操作之前就把services?.Configure()这种操作写好
所以我这里的写法可以改为:
1、先更换引用的nuget包(因为在作者修改这个问题前暂时打了一个自己的nuget)
<PackageReference Include="Unchase.Swashbuckle.AspNetCore.Extensions" Version="2.6.12" />
//改为
<PackageReference Include="Hei.Unchase.Swashbuckle.AspNetCore.Extensions" Version="2.6.12.1" />
2、上面代码改为:
var apiXmlPath = Path.Combine(AppContext.BaseDirectory, $"{Assembly.GetExecutingAssembly().GetName().Name}.xml");
//这里提前
var action = new Action<FixEnumsOptions>(o =>
{
o.IncludeDescriptions = true;
o.IncludeXEnumRemarks = true;
o.DescriptionSource = DescriptionSources.DescriptionAttributesThenXmlComments;
o.IncludeXmlCommentsFrom(apiXmlPath);
});
services.AddSwaggerGen(c =>
{
//文档
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "『xxx』后端接口:myapi",
Version = "v1",
});
c.SwaggerDoc("v2", new OpenApiInfo { Title = "v2", Version = "v2" });
c.SwaggerDoc("v3", new OpenApiInfo { Title = "v3", Version = "v3" });
c.SwaggerDoc("v4", new OpenApiInfo { Title = "华为服务号相关/健康币", Version = "v4" });
c.MapType<long>(() => new OpenApiSchema { Type = "string" });
c.IncludeXmlComments(apiXmlPath);
c.DescribeAllParametersInCamelCase();
//这里改为
c.AddEnumsWithValuesFixFilters(action);
});
问题解决!
总结
在同样.net6报这个错的时候注意看看:没页面先在build操作之前就把services?.Configure()这种操作写好