swagger-codegen icon indicating copy to clipboard operation
swagger-codegen copied to clipboard

[csharp] Mustache templates do not distinguish generated and native parent classes

Open lukket opened this issue 7 years ago • 7 comments

Description

The mustache templates do not distinguish generated and native parent classes resulting in invalid code. For example, the override modifier is set in the modelGeneric.mustach template for the ToJson method if a non-array parent exists. But the parent can be a dictionary as well (see Swagger declaration below).

public {{#parent}}{{^isArrayModel}}override {{/isArrayModel}}{{/parent}}{{^parent}}{{#discriminator}}virtual {{/discriminator}}{{/parent}}string ToJson()

Same for the validate method:

{{#parent}}
{{^isArrayModel}}
foreach(var x in BaseValidate(validationContext)) yield return x;
{{/isArrayModel}}
{{/parent}}

BaseValidate does not exist in the dictionary class.

Swagger-codegen version

Version: 2.3.0 Is it a regression? Yes, works well with version 2.3.0-20170713.173236-24.

Swagger declaration file content or url
"KeyValues": {
  "type": "object",
    "additionalProperties": {
      "type": "string"
    }
}

Generates:

public partial class KeyValues : Dictionary<String, string>,  IEquatable<KeyValues>, IValidatableObject
Command line used for generation
java -jar .\swagger-codegen-cli-2.3.0.jar generate -l csharp -i .\swagger.json -c .\config.json -o .\output\ -t .\templates\ --ignore-file-override .swagger-codegen-ignore
Steps to reproduce
  1. Create Swagger declaration with the definition mentioned above
  2. Create the code java -jar .\swagger-codegen-cli-2.3.0.jar generate -l csharp -i .\swagger.json
  3. Try to build the generated code. You will get an compiler error CS0115:
'KeyValues.ToJson()' : no suitable method found to override
Suggest a fix/enhancement

If there would be a template variable like isArrayModel but for dictionaries or native parent classes in general, we could adjust the existing templates easily.

For example like this:

{{#parent}}
{{^isArrayModel}}
{{^isDictionary}}
foreach(var x in BaseValidate(validationContext)) yield return x;
{{/isDictionary}}
{{/isArrayModel}}
{{/parent}}

lukket avatar Feb 16 '18 06:02 lukket

Could anyone please clarify if there is a known fix for this issue? thank you.

wickedw avatar Jun 04 '19 16:06 wickedw

@wickedw You can use the csharp-dotnet2 client code generator. Although it does not respect request content-types, the created code is easy to fix.

cnmicha avatar Jun 04 '19 19:06 cnmicha

I get an error similar to this on swagger codegen v3 cli. The name 'BaseValidate' does not exist in the current context

My OpenAPI spec is OpenAPI v 3.0.0. I don't use any additionalProperties, but I do have a few anyOf, allOf's. It appears that this ValidationContext function only shows up where my OpenAPI spec used anyOf. I am using swagger cli 3.0.18 from here: https://mvnrepository.com/artifact/io.swagger.codegen.v3/swagger-codegen-cli/3.0.18

rdecarreau avatar Mar 18 '20 01:03 rdecarreau

I get an error similar to this on swagger codegen v3 cli. The name 'BaseValidate' does not exist in the current context

My OpenAPI spec is OpenAPI v 3.0.0. I don't use any additionalProperties, but I do have a few anyOf, allOf's. It appears that this ValidationContext function only shows up where my OpenAPI spec used anyOf. I am using swagger cli 3.0.18 from here: https://mvnrepository.com/artifact/io.swagger.codegen.v3/swagger-codegen-cli/3.0.18

Same problem

biasc avatar May 08 '20 06:05 biasc

Same here. C# .NET Core 2.2, https://app.swaggerhub.com/apis/Billingo/Billingo/3.0.7

megant avatar Jul 09 '20 08:07 megant

I've thought that the solution would be to edit mustache template file to change BaseValidate to base.Validate However the problem is that Validate method is not virtual and is not protected.

A derived class can't get access to the base Validate method unfortunately The base IValidatableObject.Validate method can't be made protected also because of implicit interface implementation

It is possible to add a new BaseValidate method to the every model class being generated (not sure is there an option to check are there derived classes) like below:

protected IEnumerable<System.ComponentModel.DataAnnotations.ValidationResult> BaseValidate(ValidationContext validationContext)
{
    return ((IValidatableObject)this).Validate(validationContext);
}

However I do not think that implicit IValidatableObject interface implementation should be used. So another option is to make Validate method public and virtual: base

public virtual IEnumerable<System.ComponentModel.DataAnnotations.ValidationResult> Validate(ValidationContext validationContext)
{
    yield break;
}

derived

public override IEnumerable<System.ComponentModel.DataAnnotations.ValidationResult> Validate(ValidationContext validationContext)
{
    foreach(var x in base.Validate(validationContext)) yield return x;
    yield break;
}

What do you think? Which one is better ?

I've solved it right now by creating partial class with BaseValidate method implemented in the .cs file outside of auto generated Models folder. I do not like it since it requires new files to be created manually for the new Models inheritance.

Regards

oleksabor avatar Jul 17 '20 10:07 oleksabor

Please merge PR #10819 to fix this!

smargoli2 avatar Sep 12 '22 06:09 smargoli2

Just want to +1 that this is still an existing issue and a fix would be appreciated.

daveloveitk avatar Jul 19 '23 15:07 daveloveitk