domaindrivendev / Swashbuckle.AspNetCore

Swagger tools for documenting API's built on ASP.NET Core
MIT License
5.21k stars 1.3k forks source link

"dotnet swagger tofile" throws error in .net 8 with Swashbuckle v6.4 or 6.5 - says compatibility issue #2761

Closed user-shashank-providence-org closed 5 months ago

user-shashank-providence-org commented 7 months ago

I'm trying to generate swagger in postbuildevents, to do that I tried installing Swashbuckle CLI. Project config: dotnet --version: 8.0.101 Swashbuckle.Aspnetcore version: 6.4.0 Swashbuckle.Aspnetcore.Cli --> install fails saying:

Package 'Swashbuckle.AspNetCore.Cli 6.4.0' has a package type 'DotnetTool' that is not supported by project 'src\API'. and Package Swashbuckle.AspNetCore.Cli 6.4.0 is not compatible with net6.0 (.NETCoreApp,Version=v6.0). Package Swashbuckle.AspNetCore.Cli 6.4.0 supports:

  • net5.0 (.NETCoreApp,Version=v5.0) / any
  • net6.0 (.NETCoreApp,Version=v6.0) / any
  • netcoreapp2.1 (.NETCoreApp,Version=v2.1) / any
  • netcoreapp3.0 (.NETCoreApp,Version=v3.0) / any

Same issue popped when I updated the version to 6.5.0.

Also, if I don't do this via CMD and implement it in my csproj script like this:

<Target Name="PostBuild" AfterTargets="PostBuildEvent">
    <Exec Command="dotnet tool restore"></Exec>
    <Exec Command="dotnet swagger tofile --output swagger_v1.json $(OutputPath)\$(AssemblyName).dll v1" />
</Target>

It throws this error [which is not helpful at all]:

MSB3073 The command "dotnet swagger tofile --output swagger_v1.json bin\Debug\net6.0\TestSwaggerGen.dll v1" exited with code 1.

And as per Nuget's website, I can see that Swashbuckle CLI supports .net 8 but why does it say while installing the nuget package that .net 8 is not supported? image

desjoerd commented 7 months ago

This is because the tool is compiled for .NET 7. You can fix it with DOTNET_ROLL_FORWARD=LatestMajor:

  <Target Name="openapi" AfterTargets="Build">
    <Message Text="generating openapi" Importance="high" />
    <Exec Command="dotnet tool run swagger tofile --yaml --output openapi.yaml $(OutputPath)$(AssemblyName).dll v1" EnvironmentVariables="DOTNET_ROLL_FORWARD=LatestMajor" />
  </Target>

Hope this helps!

user-shashank-providence-org commented 7 months ago

This is because the tool is compiled for .NET 7. You can fix it with DOTNET_ROLL_FORWARD=LatestMajor:

  <Target Name="openapi" AfterTargets="Build">
    <Message Text="generating openapi" Importance="high" />
    <Exec Command="dotnet tool run swagger tofile --yaml --output openapi.yaml $(OutputPath)$(AssemblyName).dll v1" EnvironmentVariables="DOTNET_ROLL_FORWARD=LatestMajor" />
  </Target>

Hope this helps!

Hi @desjoerd, I've tried this approach already as discussed in this Github issue still it seems like swagger tofile keeps on looking for .Net version 7.0.0. Upon installing Swagger CLI from Nuget, it throws error saying it's not compatible with .Net 8/any.

Upon having a closer look at my output window (where I enabled debug mode in build properties), I noticed the following error response (entire stack trace): 1> Task "Exec" 1> Task Parameter:EnvironmentVariables=DOTNET_ROLL_FORWARD=LatestMajor 1> Task Parameter:Command=dotnet tool run swagger tofile --output swagger_patientv1.json bin\Debug\net8.0\X.API.dll patient 1> Environment Variables passed to tool: 1> DOTNET_ROLL_FORWARD=LatestMajor 1> dotnet tool run swagger tofile --output swagger_patientv1.json bin\Debug\net8.0\X.API.dll patient

1>    Unhandled exception. System.ArgumentException: Format of the initialization string does not conform to specification starting at index 0.
1>       at Microsoft.Data.Common.DbConnectionOptions.GetKeyValuePair(String connectionString, Int32 currentPosition, StringBuilder buffer, Boolean useOdbcRules, String& keyname, String& keyvalue)
1>       at Microsoft.Data.Common.DbConnectionOptions.ParseInternal(Dictionary`2 parsetable, String connectionString, Boolean buildChain, Dictionary`2 synonyms, Boolean firstKey)
1>       at Microsoft.Data.Common.DbConnectionOptions..ctor(String connectionString, Dictionary`2 synonyms)
1>       at Microsoft.Data.SqlClient.SqlConnectionString..ctor(String connectionString)
1>       at Microsoft.Data.SqlClient.SqlConnectionFactory.CreateConnectionOptions(String connectionString, DbConnectionOptions previous)
1>       at Microsoft.Data.ProviderBase.DbConnectionFactory.GetConnectionPoolGroup(DbConnectionPoolKey key, DbConnectionPoolGroupOptions poolOptions, DbConnectionOptions& userConnectionOptions)
1>       at Microsoft.Data.SqlClient.SqlConnection.ConnectionString_Set(DbConnectionPoolKey key)
1>       at Microsoft.Data.SqlClient.SqlConnection.set_ConnectionString(String value)
1>       at Microsoft.Data.SqlClient.SqlConnection..ctor(String connectionString)
1>       at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerConnection.CreateDbConnection()
1>       at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.get_DbConnection()
1>       at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Open(Boolean errorsExpected)
1>       at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerDatabaseCreator.<>c__DisplayClass18_0.<Exists>b__0(DateTime giveUp)
1>       at Microsoft.EntityFrameworkCore.ExecutionStrategyExtensions.<>c__DisplayClass12_0`2.<Execute>b__0(DbContext _, TState s)
1>       at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
1>       at Microsoft.EntityFrameworkCore.ExecutionStrategyExtensions.Execute[TState,TResult](IExecutionStrategy strategy, TState state, Func`2 operation, Func`2 verifySucceeded)
1>       at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerDatabaseCreator.Exists(Boolean retryOnNotExists)
1>       at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerDatabaseCreator.Exists()
1>       at Microsoft.EntityFrameworkCore.Migrations.HistoryRepository.Exists()
1>       at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
1>       at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.Migrate(DatabaseFacade databaseFacade)
1>       at X.Modules.Patients.Infrastructure.Persistence.Extensions.ServiceCollectionExtensions.AddDatabaseContext(IServiceCollection services, IConfiguration config) in C:\S2\repo\X-API\src\Modules\Patients\X.Modules.Patients.Infrastructure\Persistence\Extensions\ServiceCollectionExtensions.cs:line 38
1>       at X.Modules.Patients.Infrastructure.Persistence.Extensions.ServiceCollectionExtensions.AddPersistentServices(IServiceCollection services, IConfiguration config) in C:\S2\repo\X-API\src\Modules\Patients\X.Modules.Patients.Infrastructure\Persistence\Extensions\ServiceCollectionExtensions.cs:line 15
1>       at X.Modules.Patients.Infrastructure.PatientsModuleStartup.AddPatientsModule(IServiceCollection services, IConfiguration configuration) in C:\S2\repo\X-API\src\Modules\Patients\X.Modules.Patients.Infrastructure\PatientsModuleStartup.cs:line 29
1>       at Program.<>c__DisplayClass0_0.<<Main>$>g__InitializeModules|1() in C:\S2\repo\X-API\src\API\X.API\Program.cs:line 72
1>       at Program.<>c__DisplayClass0_0.<<Main>$>g__ConfigureServices|0() in C:\S2\repo\X-API\src\API\X.API\Program.cs:line 66
1>       at Program.<Main>$(String[] args) in C:\S2\repo\X-API\src\API\X.API\Program.cs:line 17
1>       at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
1>       at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
1>    --- End of stack trace from previous location ---
1>       at Microsoft.Extensions.Hosting.HostFactoryResolver.HostingListener.CreateHost() in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\HostFactoryResolver.cs:line 271
1>       at Microsoft.Extensions.Hosting.HostFactoryResolver.<>c__DisplayClass8_0.<ResolveHostFactory>b__0(String[] args) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\HostFactoryResolver.cs:line 75
1>       at Swashbuckle.AspNetCore.Cli.HostingApplication.GetServiceProvider(Assembly assembly) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\HostingApplication.cs:line 72
1>       at Swashbuckle.AspNetCore.Cli.Program.GetServiceProvider(Assembly startupAssembly) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\Program.cs:line 152
1>       at Swashbuckle.AspNetCore.Cli.Program.<>c.<Main>b__0_4(IDictionary`2 namedArgs) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\Program.cs:line 82
1>       at Swashbuckle.AspNetCore.Cli.CommandRunner.Run(IEnumerable`1 args) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\CommandRunner.cs:line 68
1>       at Swashbuckle.AspNetCore.Cli.CommandRunner.Run(IEnumerable`1 args) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\CommandRunner.cs:line 59
1>       at Swashbuckle.AspNetCore.Cli.Program.Main(String[] args) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\Program.cs:line 121

1> C:\S2\repo\API\src\API\X.API\X.API.csproj(50,3): error MSB3073: The command "dotnet tool run swagger tofile --output swagger_patientv1.json bin\Debug\net8.0\X.API.dll patient" exited with code -532462766.

user-shashank-providence-org commented 7 months ago

Hi @desjoerd, I forked the Swashbuckle.Aspnetcore project and ran locally and found that this same exact error as mentioned above appears from ...Cli.HostFacoryResolver file. I tried adding support for .net8.0 in <TargetFramework> for this project and packing it, but as soon as I pack this custom-swashbuckle project, it runs into the above error.

desjoerd commented 7 months ago

It looks like you're running migrations in AddDbContext -> at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.Migrate(DatabaseFacade databaseFacade) This happens because the tool actually run's your startup/program.cs logic.

A way to workaround this is to set the dotnet environment to for example openapi or any name you want. And do an if check with builder.Environment.IsEnvironment("openapi").

You should also do this for any IHostedService you register (basically you skip registering it for openapi generation). This is because it actually starts up the app (including the hosted services).

user-shashank-providence-org commented 7 months ago

It looks like you're running migrations in AddDbContext -> at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.Migrate(DatabaseFacade databaseFacade) This happens because the tool actually run's your startup/program.cs logic.

Ohh... news to me. And here I thought that the tool used to pickup the SwaggerDoc files. Thanks for this info.

A way to workaround this is to set the dotnet environment to for example openapi or any name you want. And do an if check with builder.Environment.IsEnvironment("openapi").

You should also do this for any IHostedService you register (basically you skip registering it for openapi generation). This is because it actually starts up the app (including the hosted services).

Question: If I add an exception to these services with "openapi" env settings, then this might throw an error during runtime (which it does) as it's unable to find those services. I basically kept the ASPNETCORE_ENVIRONMENT value as Development in launch settings but added a new EnvironmentVariables in my target element saying: ASPNETCORE_ENVIRONMENT=openapi

For some weird reason it seems the command dotnet swagger tofile isn't replacing the env variables from appsettings.development.json, which in my prev experience with .net 6 did work fine. I say dotnet swagger tofile coz if I remove the <target> element from .csproj then the solution runs fine and opens up the swagger UI. I'm not sure whether this is a .net-8 issue or with swagger cli Another reason why I say the above is: I tried replacing all the appsettings variables from my appsettings.json with their original values and it worked fine. Example: instead of "DbConnectionString": "#{DbConnectionString}#", I did something like: "DbConnectionString": "Data Source=....", and voila, it worked fine.

Any idea why the above would occur?

haexyh commented 7 months ago

Is a major release planned to fix this issue?

user-shashank-providence-org commented 7 months ago

Is a major release planned to fix this issue?

The workaround suggested by @desjoerd worked for me. I had to set the environment variables as mentioned above (DOTNET_ROLL_FORWARD=LatestMajor and ASPNETCORE_ENVIRONMENT=openapi) and then enclose all my services in an if condition to skip during post-build and only generate swagger file. The issue still exists if we install Swagger CLI via nuget or in target command: The fix to above I found is: [Local Build]: I had to install CLI globally using CMD then run the swagger tofile command. [On Server]: On my build pipeline, I added a task that installed CLI separately and another task to run the swagger tofile.

BrettMillerDealX commented 7 months ago

Confirming adding ENV DOTNET_ROLL_FORWARD=LatestMajor when running on docker mcr.microsoft.com/dotnet/sdk:8.0 worked for me.

d4kine commented 7 months ago

Is a major release planned to fix this issue?

@haexyh more like a minor release like for the .NET 7 issue: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/2539

Got the same issue while compiling with Debian 12 and fixed it with the environment var. I'd also appreciated a fix for this tho :)

Havunen commented 7 months ago

My fork has native support for .NET 8 as well as many fixes for it. https://github.com/Havunen/DotSwashbuckle https://www.nuget.org/packages/DotSwashbuckle.AspNetCore

user-shashank-providence-org commented 6 months ago

I tried again today by deploying my new .net 8 app with swagger tofile target to an AKS cluster which only had .net 8 SDK installed and it threw the same error.

My fork has native support for .NET 8 as well as many fixes for it. https://github.com/Havunen/DotSwashbuckle https://www.nuget.org/packages/DotSwashbuckle.AspNetCore

Adding a task beforehand to install this package was helpful but only with version 3.0.0. For 3.0.3 it throws another exception. Just an update: Installing above package with version number 3.0.0 works best.

Havunen commented 6 months ago

Just an update: Installing above package with version number 3.0.0 works best.

Can you report the exception you are seeing with the latest version here https://github.com/Havunen/DotSwashbuckle/issues please.

AnderssonPeter commented 5 months ago

It would be nice if a version was released that also targeted .net 8, currently my pipelines have to install both 7 and 8.

Havunen commented 5 months ago

DotSwashbuckle targets only .NET 8

AnderssonPeter commented 5 months ago

@Havunen sorry if I'm writing in the wrong github project, but the following is not built for .net 8 https://www.nuget.org/packages/Swashbuckle.AspNetCore.Cli/6.5.0#supportedframeworks-body-tab

martincostello commented 5 months ago

We will be adding net8.0 support in a forthcoming release.

martincostello commented 5 months ago

I'm going to close this in favour of #2792, as going through triaging the open issues I'm spotting a common theme and it's easier to manage one issue. Please track that issue for further updates.

If this issue isn't resolved once we have added support for net8.0, please open a new issue.