Nswag: via API Explorer spawning endless dotnet.exe processes

Created on 9 Jul 2018  路  5Comments  路  Source: RicoSuter/NSwag

I have previously used NSwag Studio on a Core project via "Web API or ASP.NET Core via Reflection" successfully. Today I started on a new Web API and thought I'd try out "ASP.NET Core via API Explorer", and at first all worked well.

I created the standard startup project ("dotnet new webapi") and created an NSwag Studio project for that and set it up to generate CSharp Client classes, which it did. Then I changed something, and it stopped working. I believe the only thing I changed, was the name of the controller class (from ValuesController to MyValuesController) and NSwag Studio would no longer complete the processing of the project - it would just hang.

I tried triggering it via NSwag.MSBuild, as I've done in my previous project, and the same happened there: dotnet build never completed. It just output:

Executing file 'eeapi.nswag'...

And then after a long while:

  System.InvalidOperationException: Process dotnet timed out.
     at NSwag.Commands.SwaggerGeneration.AspNetCore.Exe.RunAsync(String executable, IReadOnlyList`1 args, IConsoleHost console, Nullable`1 timeout)
     at NSwag.Commands.SwaggerGeneration.AspNetCore.ProjectMetadata.GetProjectMetadata(String file, String buildExtensionsDir, String framework, String configuration, String runtime, Boolean noBuild, IConsoleHost console) in C:\projects\nswag\src\NSwag.Commands\Commands\SwaggerGeneration\AspNetCore\ProjectMetadata.cs:line 136
     at NSwag.Commands.SwaggerGeneration.AspNetCore.AspNetCoreToSwaggerCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in C:\projects\nswag\src\NSwag.Commands\Commands\SwaggerGeneration\AspNetCore\AspNetCoreToSwaggerCommand.cs:line 65
     at NSwag.Commands.NSwagDocumentBase.GenerateSwaggerDocumentAsync() in C:\projects\nswag\src\NSwag.Commands\NSwagDocumentBase.cs:line 268
     at NSwag.Commands.NSwagDocument.ExecuteAsync() in C:\projects\nswag\src\NSwag.Commands\NSwagDocument.cs:line 81
     at NSwag.Commands.Document.ExecuteDocumentCommand.ExecuteDocumentAsync(IConsoleHost host, String filePath) in C:\projects\nswag\src\NSwag.Commands\Commands\Document\ExecuteDocumentCommand.cs:line 77
     at NSwag.Commands.Document.ExecuteDocumentCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in C:\projects\nswag\src\NSwag.Commands\Commands\Document\ExecuteDocumentCommand.cs:line 31
     at NConsole.CommandLineProcessor.ProcessSingleAsync(String[] args, Object input)
     at NConsole.CommandLineProcessor.ProcessAsync(String[] args, Object input)
     at NConsole.CommandLineProcessor.Process(String[] args, Object input)

But still, it doesn't complete.

What I found, was that both of these ways of executing the project, spawns a lot of dotnet.exe processes, in the region of around 10-20 per second. It peaked at nearly 1200 dotnet.exe processes when I managed to kill them off.

If I Ctrl+C out of the build, I get back to my command prompt, but warnings start to pop up, about every five seconds (path masked):

[PATH_TO_PROJECT][PROJECT_NAME].csproj(26,9): warning MSB4220: Waiting for the currently executing task "Exec" to cancel.

Any ideas what's going on?

All 5 comments

Switching to "Web API or ASP.NET Core via Reflection" worked like a charm. I made a zip of the project while it was still failing, if that's of interest.

With NSwag.MSBuild and api explorer you need to select the assembly not the csproj (or select nobuild) otherwise because the task compiles the csproj again and youll end up in an endless loop...

But this problem looks different... needs investigation. Can you provide this sample proj?

Sorry for the late reply (vacation time ;)).

You are right: I was specifying the .csproj and had NoBuild set to false. Changing this to true fixed the problem.

Selecting the DLL instead of the .csproj didn't work. I haven't read up on this feature, so possibly I'm missing some other setting, but it seems it still tries to interpret it as a .csproj file:

[ProjectPath]\bin\Debug\netcoreapp2.1[ProjectName].dll(1,1): error MSB4025: The project file could not be loaded. Data at the root level is invalid. Line 1, position 1.

Edit: Doh! My bad, I'd but the DLL reference in the wrong input in NSwag Studio.

I got it to work, so I'm happy :)

Just one follow up question: When would NoBuild be set to false? You say this is related to using NSwag.MSBuild, but I see the same behavior when running from NSwag Studio.

When using AspNetCoreToSwaggerGenerator via MSBuild you need to set NoBuild to True so that you dont end up in an endless build loop...

When using AspNetCoreToSwaggerGenerator via MSBuild you need to set NoBuild to True so that you dont end up in an endless build loop...

I tried this aswell with net core 3.0 build i still got into the build loop. When i added the code:

  <Target Name="NSwag" AfterTargets="Build">
    <Copy SourceFiles="@(ReferencePath)" DestinationFolder="$(OutDir)References" />
    <Message Text="Performing cmd: $(NSwagExe_Core30) run /variables:Configuration=$(Configuration),OutDir=$(OutDir) /runtime:NetCore30 /nobuild:true" />
    <Exec Command="$(NSwagExe_Core30) run /variables:Configuration=$(Configuration),OutDir=$(OutDir),nobuild=true /runtime:NetCore30 /nobuild:true" ConsoleToMSBuild="true" />
    <RemoveDir Directories="$(OutDir)References" />
  </Target>

And my configuration file would use the Api explorer and a .csproj i would get a timed out and a lot of console hosts.

Even wrote a script to remote them...

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CloseAllNetCoreHostProcesses
{
    class Program
    {
        static void Main(string[] args)
        {

            var procs = Process.GetProcesses().Where(c =>c.ProcessName.Contains("dotnet") || c.ProcessName.Contains("conhost")).OrderByDescending(x=> (x.ProcessName.Contains("dotnet") ? 1 : 0)).ToList();
            var thisProc = Process.GetCurrentProcess();

            foreach (var p in procs.Where(c=>c != thisProc))
            {
                try
                {
                    // Use debug because this will also close this window..
                    Debug.WriteLine($"{DateTime.Now:O}\tClosing\t{p.Id}\t{p.ProcessName}");
                    if(!p.CloseMainWindow())
                        p.Kill();
                }
                catch (Exception e)
                {
                    Debug.WriteLine($"{DateTime.Now:O}\tEXC: \t{e.Message}");
                }
            }

            Debug.WriteLine($"{DateTime.Now:O}\tExiting.");
        }
    }
}

When i found this post, i switched to using the assembly - that worked instantly with the above build target.

Was this page helpful?
0 / 5 - 0 ratings