I currently have an ASP.NET Core API fully working and now I want to generate a C# client based on my API but instead of generating custom models I want to re-use a Net Standard 2.0 class library which already contains all the models/classes which are used by the ASP.NET Core API routes.
What can I do so it uses my classes instead of self generated ones?
You have to manually exclude them all with ExcludedTypeNames and add the required usings with AdditionalNamespaceUsages
Or just disable GenearteDtoTypes
I have generate DTO types disabled but when generating the swagger itself it uses custom definitions for everything, like I have a User entity but the swagger definitons calls it AccountModel
How can I change it that it uses the actual names and not custom made up names?
Also some parameters are generic for those it generates names as follows:
GenericThing<User> => GenericThingOfUser instead of GenericThing<User>
For the issue with generic types, I'm modifying the generated client file with a regex replace like this:
System.Text.RegularExpressions.Regex.Replace(generatedApiClientText, "([A-Za-z]+)Of([a-zA-Z]+)", "$1<$2>");
That's called in my build script, right after the NSwag client generation is executed.
For the first issue with using your own namespaces and models from a shared library, you can edit your nswag.json to look something like this:
{
"runtime": "NetCore21",
"swaggerGenerator": {
"fromSwagger": {
"json": "",
"url": "swagger.json",
"output": null
}
},
"codeGenerators": {
"swaggerToCSharpClient": {
"additionalNamespaceUsages": [
"LightQuery.Client"
],
"excludedTypeNames": [
"UserModel"
]
}
}
}
I thought I could workaround it by overriding the TypeNameGenerator used when generating the SwaggerDoc, it does change the model names shown in the SwaggerDoc now but when using NSwagStudio to generate a C# Client based on the new SwaggerDoc it just changes the names back to what they where before I changed them using the TypeNameGenerator.
CustomTypeNameGenerator:
```C#
private const string Pattern = @"Of\s([^\n\r\0])";
public override string Generate(JsonSchema4 schema, string typeNameHint, IEnumerable
{
string output;
switch (typeNameHint)
{
case var val when Regex.IsMatch(typeNameHint, Pattern):
output = Regex.Replace(val, Pattern, @"<$1>");
break;
default:
output = base.Generate(schema, typeNameHint, reservedTypeNames);
break;
}
return output;
}
```
Example:
Type: Response<Device>
typeNameHint: ResponseOfDevice
output: Response<Device>
But NSwagStudio after generation
Type: ResponseOfDevice
Why does this happen?
I got everything else working, except this.
I want to eventually make the generation of the clients part of the build process so it always stays up to date with the API
The problem is that NSwagStudio does not pick up this setting, but uses the setting from the config file, I think with this PR, your scenario is supported (you can use your generator in CLI/NSwagStudio): https://github.com/RSuter/NSwag/pull/1655
I was looking into the code of NSwag and NJsonSchema and I concluded the same, it uses the DefaultTypeNameGenerator
I looked at the PR but how does this solve it?
How can I use it to generate new clients after building the API project using my CustomTypeNameGenerator?
Ah no this customization only works for schema generation, not for code generation. I think the best option for you (provide CustomTypeNameGenerator) and use this via cli is to build your own command line tool because you cannot inject custom class instances at the moment. This feature is tracked here: https://github.com/RSuter/NSwag/issues/924
Ah okay - So I'll just have to make a DotNet Core or DotNet Standard Console project which I run after the build finishes to generate my classes? Right?
Is it possible to use a NSwag Configuration Document when using the C# SwaggerToCSharpClientGenerator class?
I think this should be possible, the NSwagDocument is in NSwag.Commands i think. The actual command instance exposes a Settings object
Ah okay - I'll check - currently trying to find where to set the CustomTypeNameGenerator on the SwaggerToCSharpClientGenerator
Or is it the ParameterNameGenerator? It's a different interface but same methods
its in SwaggerToCSharpClientGeneratorSettings.TypeNameGenerator i thikn
If it's there it's not public - I can't find it or I'm just stupidly looking over it
It's under settings.CodeGeneratorSettings.TypeNameGenerator
CodeGeneratorSettings doesnt make much sense (legacy) but it's mainly because you're leaving NSwag settings and entering NJsonSchema settings (DTO schema/code generator)
Ah - thanks!
Still looking on how to load the .nswag document
Use the NSwagDocument class (NSwagDocument.LoadAsync()) from NSwag.Commands package
Lolz - not it shows, didn't show any methods for me first - thanks, will report back if I find any problems
Thanks for your help @RSuter! I've got it all working, even post-build event and such...
Sadly had to couple somethings in my code to get it fully working with generic return types - all because Swagger doesn't support Generics 馃槣
If needed I can share the code 馃榿
For anyone else looking to implement @GeorgDangl RegEx here is the MSBuild code:
First add a <UsingTask> node similar to the following in your project file:
<UsingTask TaskName="TokenReplace" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<Path ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
string content = File.ReadAllText(Path);
content = System.Text.RegularExpressions.Regex.Replace(content, "([A-Za-z]+)Of([a-zA-Z]+)", "$1<$2>");
content = System.Text.RegularExpressions.Regex.Replace(content, "([A-Za-z]+)And([a-zA-Z]+)", "$1,$2");
File.WriteAllText(Path, content);
]]>
</Code>
</Task>
</UsingTask>
Then in your MSBuild target, reference the task using something like the following:
<TokenReplace Path="$(SolutionDir)yourClient.cs" />
Most helpful comment
For anyone else looking to implement @GeorgDangl RegEx here is the MSBuild code:
First add a <UsingTask> node similar to the following in your project file:
Then in your MSBuild target, reference the task using something like the following: