Skip to content

feat(csharp): idiomatic C# code generation for scalar types, validators, and defaults#231

Merged
jamieshorten merged 6 commits into
accordproject:mainfrom
muhabdulkadir:moh/fix-csharp
May 25, 2026
Merged

feat(csharp): idiomatic C# code generation for scalar types, validators, and defaults#231
jamieshorten merged 6 commits into
accordproject:mainfrom
muhabdulkadir:moh/fix-csharp

Conversation

@muhabdulkadir
Copy link
Copy Markdown
Contributor

@muhabdulkadir muhabdulkadir commented May 22, 2026

Closes #

Summary

Significantly improves the C# code generator to produce idiomatic, type-safe C# from Concerto models. This PR adds DataAnnotations validation attributes, and emits default property initializers.

Changes

  • Scalar struct validators
    When a scalar declares a validator (regex, length, range), the Value property is explicitly declared with the corresponding DataAnnotations attribute:

    public readonly record struct SSN(string Value)
    {
        [System.ComponentModel.DataAnnotations.RegularExpression(@"\d{3}-\d{2}-\d{4}", ErrorMessage = "Invalid characters")]
        public string Value { get; init; } = Value;
        ...
    }
    
  • Numeric range validators on fields
    Integer, Long, and Double fields with a range validator emit [Range]:

    [System.ComponentModel.DataAnnotations.Range(typeof(int), "1", "100")]
    public int salary { get; set; }
    

    Open bounds (only min or only max declared) fill to the type's natural min/max.

  • Default property initializers
    Fields with a default value emit a C# property initializer:

    public string name { get; set; } = "Alice";
    public SSN ssn { get; set; } = new("000-00-00-0000");
    

    For scalar-typed fields, the field-level default takes precedence over the scalar declaration's default.

Author Checklist

  • Ensure you provide a DCO sign-off for your commits using the --signoff option of git commit.
  • Vital features and changes captured in unit and/or integration tests
  • Commits messages follow AP format
  • Extend the documentation, if necessary
  • Merging to main from fork:branchname

Signed-off-by: muhammed-abdulkadir <muhammed.abdulkadir@docusign.com>
Signed-off-by: muhammed-abdulkadir <muhammed.abdulkadir@docusign.com>
Signed-off-by: muhammed-abdulkadir <muhammed.abdulkadir@docusign.com>
@jamieshorten
Copy link
Copy Markdown
Contributor

How does an entity/instance deserialise/serialise to this

e.g

namespace org.acme.hr@1.0.0
scalar SSN extends String default="000-00-0000" regex=/\d{3}-\d{2}-\{4}+/

concept Person identified by ssn {
    o SSN ssn
    o String givenName
}

would have an instance like

{
   "$class": "org.acme.hr@1.0.0.Person"
    "ssn" : "000-00-0000",
    "givenName": "Jamie"
}

@muhabdulkadir
Copy link
Copy Markdown
Contributor Author

How does an entity/instance deserialise/serialise to this

e.g

namespace org.acme.hr@1.0.0
scalar SSN extends String default="000-00-0000" regex=/\d{3}-\d{2}-\{4}+/

concept Person identified by ssn {
    o SSN ssn
    o String givenName
}

would have an instance like

{
   "$class": "org.acme.hr@1.0.0.Person"
    "ssn" : "000-00-0000",
    "givenName": "Jamie"
}

Thanks! I'd add a converter

@jamieshorten
Copy link
Copy Markdown
Contributor

we should have a round trip test for serialisation/deserialisation of instances to some generated classes. The metamodel would be one candidate...

…t for serialisation

Signed-off-by: muhammed-abdulkadir <muhammed.abdulkadir@docusign.com>
Signed-off-by: muhammed-abdulkadir <muhammed.abdulkadir@docusign.com>
Signed-off-by: muhammed-abdulkadir <muhammed.abdulkadir@docusign.com>
@jamieshorten jamieshorten merged commit a7a8d25 into accordproject:main May 25, 2026
11 checks passed
@muhabdulkadir muhabdulkadir deleted the moh/fix-csharp branch May 25, 2026 13:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants