RicoSuter / NSwag

The Swagger/OpenAPI toolchain for .NET, ASP.NET Core and TypeScript.
http://NSwag.org
MIT License
6.79k stars 1.29k forks source link

Circular dependency problem with Blazor WASM (Hosted) #3188

Open hypervtechnics opened 3 years ago

hypervtechnics commented 3 years ago

Disclaimer: This is not an issue regarding the functionality. It's more of a problem with the MSBuild integration introduced by the new Blazor WASM (Hosted) template.

I currently have two projects:

1. ASP.NET Core 3.1 Web API + Xamarin

I am using NSwag to automatically generate the C# client in the first project. This is done using an MSBuild target which generates code into a C# Client project. This client project is then used by Xamarin. This works! (Very good! Thanks to Rico Suter for NSwag!)

I currently have my Xamarin project in a separate solution. The client library is pushed to a NuGet feed and then consumed by the Xamarin application. So they are separated. Using this flow makes the usage in the daily workflow very easy.

2. ASP.NET Core 5 Web API + Blazor WebAssembly (Hosted in WebAPI)

Now I started another project and decided to go for an easy to use application (api + frontend shipped from the server - easier to setup). Therefore I made use of the Blazor WASM (ASP.NET Core Hosted) template in Visual Studio. I decided that having the web client (blazor) in the same solution is an acceptable solution regarding debugging and the fact that they are also shipped together. This works like so: The WebAPI project has a reference to the Blazor project to be able to copy it's compiled output during compilation and include it in the WebAPI project. Another library (referenced by the webAPI and using a middleware from that library) then serves the files from this directory which enables me to serve both the api and the frontend static files from my web api project. Blazor is written in C# and therefore compiled.

Introduction to my problem: I want to use the same approach as in the first project. Except the frontend won't consume the NuGet library. Instead it should directly consume the client project. Any optional client in the future then can still consume the NuGet client library.

Now really for my problem: The reference from the webapi to the blazor project is a problem. The NSwag.MSBuild can't work on the compiled output from the web api to generate the client because the blazor project is not yet compiled (as mentioned it's a dependency). The Blazor project is not yet compiled because it does not have the updated client yet.

What I considered: Kind of work around it by decoupling some parts and having to build twice for one real build (generate client + real build). Decoupling includes removing NSwag.MSBuild. The workflow would then be:

  1. Run the project (web api)
  2. Use NSwag Studio with the locally served Swaggerfile to manually regenerate the client
  3. Stop project
  4. Restart project to make blazor to pick up the new changes in the client
  5. Now you got the real build

Do some of you have an idea on how to have a clean solution? The current one is kind of loophole. Am I overseeing something? Having to build a project twice to really start debugging seems wrong to me.

I also asked the question here: https://stackoverflow.com/questions/64927255

Afroshar commented 3 years ago

I have the exact same problem. Doesn't make much sense that you have to build the BlazorWASM client project to build the server part, since the output is gonna be just static files as I understand. I basically did what you suggested (build 2 times every time to get the real build). Maybe you can somehow take out the dependency and use MSBuild targets to manually put static files where you need them? Probably should ask Blazor devs about this

hypervtechnics commented 3 years ago

FYI: I separated the Blazor part into separate server /project to work around this issue.

Afroshar commented 3 years ago

you mean you put your WebApi controllers in a different project? How does that work exactly?

hypervtechnics commented 3 years ago

Not the WebApi controllers. The whole magic works because you have a dependency on the Client Project and a NuGet package which provides you a middleware (look at the Startup class) which then evaluates the reference to auto setup the client. I created a second ASP.NET Core WebAPI without any additional controllers just using the middleware and 404 catch all route. This project references the client just as before.

The down side: Frontend and Backend are separate servers and have to be run in parallel.

Afroshar commented 3 years ago

I see, I have to put my app in a Docker container and I really don't want to mess with multiple containers (or multiple apps in one container), so this approach is not for me unfortunately. I think it's possible to put controller classes in a different project and load them in your webapi in startup.cs, maybe this way NSwag doesn't need to build webapi project to genereate client code...

RicoSuter commented 3 years ago

Just some info: NSwag for core is not really reflection based as it has to actually run part of the asp net app to get hold of the asp api explorer which describes the api. But even if it could work on dlls (reflection) you’d probably have the same circular problem because you have to build this dll.

hexxone commented 2 years ago

Hey there, the Issue might be a bit old by now; but I had a similar problem and solved it by setting an inline variable on Build.

Cheers :)

E.g.:

<PropertyGroup>
    <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
</PropertyGroup>

<Target Name="NSwag" AfterTargets="PostBuildEvent" Condition=" '$(NO_RECURSE)' != 'true' ">
    <Exec WorkingDirectory="$(ProjectDir)" EnvironmentVariables="NO_RECURSE=true" Command="$(NSwagExe_Net60) run ../nswag.nswag /variables:Configuration=Release" />
</Target>
girakun commented 1 year ago

I think this is still a problem in 2023 as i can't find a solution for this while searching.

We also have a hosted webassembly and want to generate a httpclient in the wasm project that host reference to as it hosts it. But the host does not use anything of the wasm code, the shared code is in another project.

What we did was:

  1. Remove the reference from host => wasam
  2. On post build of host generate a httpclient using nswag into the wasm project.
  3. On post build of the wasm project we copied the x.x.x.staticwebassets.runtime.json in /bin to /bin to the host project and renamed it to {host project}.staticwebassets.runtime.json.
  4. This way the host project still serves the static files from the wasm project without having a reference to it, and wont need multiple builds and stuff like that.

May not work for everyone depending on what reference the wasm project or if the host serves other static files, but worked for us.

clbarrett commented 1 year ago

I'm running into a similar problem though without any Blazor.

  1. Created a C# ASP .Net Core Web API project running .Net 6.0
  2. Added the relevant nuget packages. NSwag.MSBuild being the primary one.
  3. Generated a simple nswag.json using the NSwagStudio targetting this project.
  4. Used the following Target inside the csproj file: \<Target Name="NSwag" AfterTargets="PostBuildEvent"> \<Exec WorkingDirectory="$(ProjectDir)" EnvironmentVariables="ASPNETCORE_ENVIRONMENT=Development;NO_RECURSE=true" Command="$(NSwagExe_Net60) run nswag.json" /> \</Target>

From there it loops through my controller, program.cs and two classes endlessly until the dotnet build times out. Really confused what I'm doing wrong here.