domaindrivendev / Swashbuckle.AspNetCore

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

swagger tofile getting an error on System.Runtime.Loader #1485

Closed somanysteves closed 4 years ago

somanysteves commented 4 years ago

Hi all, great tool idea. I'm trying to get it working. Right now I have the latest 5.0 installed.

doing a dotnet publish -o ./pub swagger tofile --output .\swagger.json .\pub\bin\myfile.dll v1

And getting a: Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 'System.Runtime.Loader, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040) at Swashbuckle.AspNetCore.Cli.Program.<>c.

b__0_3(IDictionary2 namedArgs) at Swashbuckle.AspNetCore.Cli.CommandRunner.Run(IEnumerable1 args) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\CommandRunner.cs:line 68 at Swashbuckle.AspNetCore.Cli.CommandRunner.Run(IEnumerable`1 args) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\CommandRunner.cs:line 68 at Swashbuckle.AspNetCore.Cli.Program.Main(String[] args) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\Program.cs:line 106

ryeleo commented 4 years ago

I'm seeing this issue as well when I tried using the 5.0 CLI tool downloaded from nuget.

I tried building the tool locally to debug, then I got a similar-but-different error:

$ swagger.exe tofile --output test.json "C:\Users\rleonar7\dev\UO.CAS.PSY.Duckling\UO.CAS.PSY.Duckling\bin\Release\netcoreapp2.2\UO.CAS.PSY.Duckling.dll" 1.0.0

Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 'Microsoft.AspNetCore.Hosting.Abstractions, Version=3.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
   at Swashbuckle.AspNetCore.Cli.Program.<>c.<Main>b__0_3(IDictionary`2 namedArgs)
   at Swashbuckle.AspNetCore.Cli.CommandRunner.Run(IEnumerable`1 args) in C:\Users\rleonar7\dev\Swashbuckle.AspNetCore\src\Swashbuckle.AspNetCore.Cli\CommandRunner.cs:line 68
   at Swashbuckle.AspNetCore.Cli.CommandRunner.Run(IEnumerable`1 args) in C:\Users\rleonar7\dev\Swashbuckle.AspNetCore\src\Swashbuckle.AspNetCore.Cli\CommandRunner.cs:line 59
   at Swashbuckle.AspNetCore.Cli.Program.Main(String[] args) in C:\Users\rleonar7\dev\Swashbuckle.AspNetCore\src\Swashbuckle.AspNetCore.Cli\Program.cs:line 106
ducka commented 4 years ago

I've been battling with this issue myself. It appears to occur when you have dotnet 3.0 installed on your system, and you're trying to extract Open API specs out of a Startup Assembly that targets dotnet 2.*.

I assume when you invoke the swagger cli tool it's using dotnet 3.0 to host the tool, but then it has to load a dotnet 2.* startup assembly to extract the specs, and somewhere in this process it encounters assembly loading issues because of it.

ryeleo commented 4 years ago

That's an interesting theory. I'm planning to run this tool in Azure DevOps: I'll give it a shot and see if the tool works in the more 'vanilla' environment provided by the DevOps Agent (as opposed to my local development machine).

UPDATE: Running this in Azure DevOps still kicked-back the same exception that we were originally seeing:

Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 'System.Runtime.Loader, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
   at Swashbuckle.AspNetCore.Cli.Program.<>c.<Main>b__0_3(IDictionary`2 namedArgs)
   at Swashbuckle.AspNetCore.Cli.CommandRunner.Run(IEnumerable`1 args) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\CommandRunner.cs:line 68
   at Swashbuckle.AspNetCore.Cli.CommandRunner.Run(IEnumerable`1 args) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\CommandRunner.cs:line 68
   at Swashbuckle.AspNetCore.Cli.Program.Main(String[] args) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\Program.cs:line 106

I'm unsure... the Azure DevOps "vs2017 Hosted Agent" environment I am using might still have a 3.x verion(s) of the dotnet SDK installed.

danielloganking commented 4 years ago

I am trying to use Swashbuckle.AspNetCore.Cli --version 5.3.1 to generate the swagger document for a netcoreapp2.2 project and encounter this error. I did some quick triage and deleted system.runtime.loader from my user's .nuget folder then did dotnet restore in the directory with the tool installed. Afterwards system.runtime.loader but it contained version 4.0.0 rather than the expected 4.1.0. I expect there is a discrepancy in the version of system.runtime.loader in the csproj of the CLI source code and what is listed on the nuspec manifest but haven't confirmed that hypothesis.

EDIT: I confirmed that the .csproj for Swashbuckle.AspNetCore.Cli does reference System.Runtime.Loader v4.3.0

It's also entirely possible that MS has diverged the assembly version and the nuget package version such that the error message for 4.1.0.0 is actually indicating that 4.3.0 is missing as there is no publically available 4.1.0 version of the package, only 4.0.0 and 4.3.0.

Lonli-Lokli commented 4 years ago

We see that issue occurs only with binaries created for CLI with target = netcoreapp3.0 Why do we need both? Tool is created with one version only. When I'm trying to run CLI with target = netcoreapp2.1 everything is fine.

Also, just to note, we cannot call this CLI as mentioned in readme, eg dotnet swagger, because this syntax requires command to be dotnet-swagger, not swagger.

If the command begins with the prefix dotnet-, an alternative way to invoke the tool is to use the dotnet command and omit the tool command prefix. For example, if the command is dotnet-doc, the following command invokes the tool:

domaindrivendev commented 4 years ago

I've been able to run the CLI tool successfully for applications targeting netcoreapp2.1, netcoreapp3.0 and netcoreapp3.1. So, if there's an issue here, the repro steps are more nuanced than running against a certain application target.

Could someone on this thread please put together a minimal application that repro's the issue and then post it to github where I can pull it down and investigate further. Without this, I won't be able help.

Lonli-Lokli commented 4 years ago

@domaindrivendev here is it https://github.com/Lonli-Lokli/SwaggerCLI

Please note that I have only 1 SDK installed, 3.1. It might be the reason why you do not see this error.

ryeleo commented 4 years ago

I just built up a simple example application using asp.net core 3.1 and did not see the issue. I'm going to reproduce the issue in my original project and see if I can extract a simplified example. The original project targets framework netcoreapp2.2

Lonli-Lokli commented 4 years ago

@domaindrivendev Were you able to reproduce the issue?

domaindrivendev commented 4 years ago

Yes was able to repro. However, I don’t have bandwidth right now to explore a solution. Assistance in this area would be very helpful

lewinskimaciej commented 4 years ago

I have the same issue right now. Using 5.3.1 Swashbuckle.AspNetCore.Cli, project targets netcoreapp2.2, getting:

0>Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 'System.Runtime.Loader, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
0>   at Swashbuckle.AspNetCore.Cli.Program.<>c.<Main>b__0_3(IDictionary`2 namedArgs)
0>   at Swashbuckle.AspNetCore.Cli.CommandRunner.Run(IEnumerable`1 args) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\CommandRunner.cs:line 68
0>   at Swashbuckle.AspNetCore.Cli.CommandRunner.Run(IEnumerable`1 args) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\CommandRunner.cs:line 68
0>   at Swashbuckle.AspNetCore.Cli.Program.Main(String[] args) in C:\projects\ahoy\src\Swashbuckle.AspNetCore.Cli\Program.cs:line 107
Lonli-Lokli commented 4 years ago

@domaindrivendev Why do we need 2 targets for this CLI, netcoreapp2.1 and netcoreapp3.1 ? This issue happens only with 3.1 version, which is actually packed as .NET CLI tool.

Lonli-Lokli commented 4 years ago

@domaindrivendev may I kindly ask you how to reproduce this issue locally with code build from this cloned repo and not from installed global tool? I would like to play with possible options.

Eg

  1. Clone https://github.com/domaindrivendev/Swashbuckle.AspNetCore
  2. Clone & build project with failure https://github.com/Lonli-Lokli/SwaggerCLI
  3. ??? build & run CLI with specific commands
nattomi commented 4 years ago

I've also came accross this problem and created a minimal working example which can be used for reproducing the issue: https://github.com/nattomi/SwaggerCliFailMwe.

domaindrivendev commented 4 years ago

@domaindrivendev may I kindly ask you how to reproduce this issue locally with code build from this cloned repo and not from installed global tool? I would like to play with possible options.

As I've realized this is a more braodly impacting problem than I first thought, I've moved it up the list of priorities and have started to investigate further. @Lonli-Lokli - I'm still looking for a way to repro against source, will let you know what I come up with

domaindrivendev commented 4 years ago

@Lonli-Lokli to repro from source:

> git clone git@github.com:domaindrivendev/Swashbuckle.AspNetCore.git
> cd Swashbuckle.AspNetCore
> dotnet build
> cd test\WebSites\NetCore21

Simulate running via 2.1 SDK - works fine ...

> dotnet ..\..\..\src\Swashbuckle.AspNetCore.Cli\bin\Debug\netcoreapp2.1\dotnet-swagger.dll tofile .\bin\Debug\netcoreapp2.1\NetCore21.dll v1

Simulate running via 3.x SDK - fails ...

> dotnet ..\..\..\src\Swashbuckle.AspNetCore.Cli\bin\Debug\netcoreapp3.0\dotnet-swagger.dll tofile .\bin\Debug\netcoreapp2.1\NetCore21.dll v1
domaindrivendev commented 4 years ago

@Lonli-Lokli going back to a point you made further back up the thread - it seems I could have the CLI project target netcoreapp2.1 only and that would be enough to support all combinations of SDK and application framework versions. Doing more tests now to confirm:

@domaindrivendev Why do we need 2 targets for this CLI, netcoreapp2.1 and netcoreapp3.1 ? This issue happens only with 3.1 version, which is actually packed as .NET CLI tool.

Lonli-Lokli commented 4 years ago

@domaindrivendev the only thing that I see might be broken with single targetframework is possibility to install it as a local tool. Btw I've checked with DotPeek swagger cli for the netcore3 and it appears it referencing some of netcore2.1 libs, not sure why, mb because of referenced project.

domaindrivendev commented 4 years ago

@Lonli-Lokli I tested the local tool installation with the single target and it appears to work just fine. I have a preview package 5.4.0-preview-1253 sitting up on myget.org. If you get a chance, could you try uninstalling your current version of the tool and installing the preview instead, and letting me know if that works?

nattomi commented 4 years ago

@domaindrivendev, I've tested my mwe with 5.4.0-preview-1253 and the Cli tool now works on the project targeting netcoreapp2.2 as well. See updated version: https://github.com/nattomi/SwaggerCliFailMwe Will test and see if it works on my real project too.

riker09 commented 4 years ago

Stupid question (maybe): How to I upgrade to 5.4.0-preview-1253? This version is not on NuGet, is it?

I have updated the version in my dotnet-tools.json and in my *.csproj.

dotnet tool restore
error NU1102: Das Paket "swashbuckle.aspnetcore.cli" der Version (= 5.4.0-preview-1253) wurde nicht gefunden.
error NU1102:   - 23 Version(en) gefunden in "nuget.org" [ Nächste Version: 5.3.3 ]
error NU1102:   - 0 Version(en) gefunden in C:\Program Files\dotnet\sdk\NuGetFallbackFolder.

Das Paket "swashbuckle.aspnetcore.cli" konnte nicht wiederhergestellt werden. Ursache: Microsoft.DotNet.ToolPackage.ToolPackageException: Das Toolpaket konnte nicht wiederhergestellt werden.
   at Microsoft.DotNet.Tools.Tool.Install.ProjectRestorer.Restore(FilePath project, PackageLocation packageLocation, String verbosity)
   at Microsoft.DotNet.ToolPackage.ToolPackageInstaller.InstallPackageToExternalManagedLocation(PackageLocation packageLocation, PackageId packageId, VersionRange versionRange, String targetFramework, String verbosity)
   at Microsoft.DotNet.Tools.Tool.Restore.ToolRestoreCommand.InstallPackages(ToolManifestPackage package, Nullable`1 configFile)

Fehler bei der Wiederherstellung.

Sorry for German output. 😅

nattomi commented 4 years ago

@riker09 Add a nuget.config file like this: `<?xml version="1.0" encoding="utf-8"?>

`

riker09 commented 4 years ago

Okay, I was able to add the NuGet package with this command: dotnet nuget add source --name domaindriverdev https://www.myget.org/F/domaindrivendev/api/v3/index.json

I removed ./bin and ./obj afterwards, then ran dotnet restore MyProject.csproj and dotnet build MyProject.csproj. The error described in this issue went away, however I'm facing a new one:

PS C:\Users\MyUserName\Projects\MyRepo\MyService> dotnet swagger tofile --output swagger.json .\bin\Debug\netcoreapp2.2\MyService.dll v1

Unhandled Exception: StructureMap.Building.StructureMapBuildException: Error while building type Swashbuckle.AspNetCore.SwaggerGen.ConfigureSwaggerGeneratorOptions.  See the inner exception for details
1.) new ConfigureSwaggerGeneratorOptions(*Default of IOptions<SwaggerGenOptions>*, *Default of IServiceProvider*, *Default of IHostingEnvironment*)
2.) Swashbuckle.AspNetCore.SwaggerGen.ConfigureSwaggerGeneratorOptions
3.) Instance of IConfigureOptions<SwaggerGeneratorOptions> (Swashbuckle.AspNetCore.SwaggerGen.ConfigureSwaggerGeneratorOptions)
4.) All registered children for IEnumerable<IConfigureOptions<SwaggerGeneratorOptions>>
5.) Instance of IEnumerable<IConfigureOptions<SwaggerGeneratorOptions>>
6.) new OptionsFactory`1(*Default of IEnumerable<IConfigureOptions<SwaggerGeneratorOptions>>*, *Default of IEnumerable<IPostConfigureOptions<SwaggerGeneratorOptions>>*, *Default of IEnumerable<IValidateOptions<SwaggerGeneratorOptions>>*)
7.) OptionsFactory<SwaggerGeneratorOptions> ('48905920-c9cd-4ae8-ace8-eaeebb281f4f')
8.) Instance of IOptionsFactory<SwaggerGeneratorOptions> ('48905920-c9cd-4ae8-ace8-eaeebb281f4f')
9.) new OptionsManager`1(*Default of IOptionsFactory<SwaggerGeneratorOptions>*)
10.) OptionsManager<SwaggerGeneratorOptions> ('991c1d92-470e-4242-8e09-20152b229dd7')
11.) Instance of IOptions<SwaggerGeneratorOptions> ('991c1d92-470e-4242-8e09-20152b229dd7')
12.) Container.GetInstance(IOptions<SwaggerGeneratorOptions>)
13.) Lambda: Invoke(value(StructureMap.ContainerExtensions+<>c__DisplayClass9_0).descriptor.ImplementationFactory, IContext.GetInstance())
14.) Instance of Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions (System.Object)
15.) new SwaggerGenerator(*Default of SwaggerGeneratorOptions*, *Default of IApiDescriptionGroupCollectionProvider*, *Default of ISchemaGenerator*)
16.) Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator
17.) Instance of Swashbuckle.AspNetCore.Swagger.ISwaggerProvider (Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator)
18.) Container.GetInstance(Swashbuckle.AspNetCore.Swagger.ISwaggerProvider)
 ---> System.IO.FileNotFoundException: Could not find file 'C:\Users\MyUserName\Projects\MyRepo\MyService\bin\Debug\netcoreapp2.2\dotnet-swagger.xml'.

The file dotnet-swagger.xml does indeed not exist. My guess is that it should be MyService.xml instead?

Full disclosure: I'm a noob regarding dotnet tooling and C# development in general. So please forgive me when I'm doing any mistakes that should be obvious. 😅


[EDIT] There is another issue regarding this: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1518, however it was closed due to no proper steps for reproduction. Well, here we are.

domaindrivendev commented 4 years ago

@riker09 could you create a minimal application that repro's your issue and post to github so I can pull down and troubleshoot? That's been the most efficient approach for getting to the bottom of these obscure issues.

domaindrivendev commented 4 years ago

That said - have you configured SB to use Xml comments? If so, I wonder if there's something wrong with the way your detecting the XML comments file name. If you could provide a snippet of your SB configuration we could rule that out first?

riker09 commented 4 years ago

Okay, Swashbuckle Startup configuration first. I'll provide a repo with a minimal reproduction app afterwards.

ConfigureServices method:

            // Register the Swagger generator, defining 1 or more Swagger documents
            services.AddSwaggerGen(c =>
            {
                var title = Assembly.GetEntryAssembly().GetName().Name;
                c.SwaggerDoc("v1", new OpenApiInfo { Title = title, Version = "v1" });

                // Set the comments path for the Swagger JSON and UI.
                var xmlFile = $"{title}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                c.IncludeXmlComments(xmlPath);

                c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
            });

and in Configure method:

            // Enable middleware to serve generated Swagger as a JSON endpoint.
            app.UseSwagger();

            // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
            // specifying the Swagger JSON endpoint.
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
            });
riker09 commented 4 years ago

Okay, I have found my mistake. I'm developing a microservice architectured application and moved the Swagger boilerplate code to a support project which is then included in the API projects. Everything is a monorepo.

Like I said, I'm no C# expert and things like Assembly.GetExecutingAssembly().GetName().Name or GetEntryAssembly() don't play together nicely with that. This was my extension method before:

using Microsoft.OpenApi.Models;
using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Microsoft.Extensions.DependencyInjection
{
    public static partial class ServiceCollectionExtensions
    {
        public static IServiceCollection AddDmforce4SwaggerSupport(this IServiceCollection @this)
        {
            // Register the Swagger generator, defining 1 or more Swagger documents
            @this.AddSwaggerGen(c =>
            {
                var title = Assembly.GetEntryAssembly().GetName().Name;
                c.SwaggerDoc("v1", new OpenApiInfo { Title = title, Version = "v1" });

                // Set the comments path for the Swagger JSON and UI.
                var xmlFile = $"{title}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                c.IncludeXmlComments(xmlPath);

                c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
            });

            return @this;
        }
    }
}

And this is it after:

using Microsoft.OpenApi.Models;
using System;
using System.IO;
using System.Linq;

namespace Microsoft.Extensions.DependencyInjection
{
    public static partial class ServiceCollectionExtensions
    {
        public static IServiceCollection AddDmforce4SwaggerSupport(this IServiceCollection @this, string title)
        {
            // Register the Swagger generator, defining 1 or more Swagger documents
            @this.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = title, Version = "v1" });

                // Set the comments path for the Swagger JSON and UI.
                var xmlFile = $"{title}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                c.IncludeXmlComments(xmlPath);

                c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
            });

            return @this;
        }
    }
}

I hope this is helpful for somebody. My issue is hereby resolved.

tariqzafa700 commented 3 years ago

If it helps any body you can resolve this issue by using the right version of dotnet sdk. If your project uses dotnet 2.1 as target run time, you should use the same to install the tool and also running the swagger generation command. Thats how this error resolved for me. The developers have put this as a note too in the documentation.

fauresco commented 2 years ago

Expanding on tariqzafa700 answer, make sure you have a global.json file in your project so when you issue a "dotnet" cmd you are using the correct version of the SDK instead of the latest version installed in your machine. For instance, if your project is .net 5, then you must use 5.0.x SDK:

{
  "sdk": {
    "version": "5.0.0",
    "rollForward": "latestFeature"
  }
}

In my case, the dotnet version used is going to be 5.0.2 instead of 6.0.1 which is the latest I have at the moment.

More info on this here.