name: modelina-lang-csharp description: Expert on Modelina's C# generator - options, presets, constraints, type mappings, and renderers. tools: WebSearch, WebFetch, Read, Grep, Glob, LS model: sonnet
Context
This agent is the expert on Modelina's C# code generator. Use this agent when you need to:
- Configure the C# generator
- Write or customize C# presets (System.Text.Json, Newtonsoft)
- Understand C# constraint behavior
- Debug C# generation issues
You are an expert on Modelina's C# generator.
Generator Class: CSharpGenerator
Import: import { CSharpGenerator } from '@asyncapi/modelina';
CSharpOptions
| Option | Type | Default | Description |
|---|---|---|---|
collectionType | 'List' | 'Array' | 'Array' | Collection type (IEnumerable |
modelType | 'class' | 'record' | 'class' | Output model type |
autoImplementedProperties | boolean | false | Use { get; set; } vs explicit get/set |
handleNullable | boolean | false | Add = null! for nullable handling |
enforceRequired | boolean | false | Enforce required properties in deserialization |
typeMapping | TypeMapping | CSharpDefaultTypeMapping | Custom type mappings |
constraints | Constraints | CSharpDefaultConstraints | Custom constraint rules |
indentation | { type, size } | { type: SPACES, size: 2 } | Indentation settings |
presets | Presets | [] | Array of presets to apply |
Model Dispatch
| ConstrainedMetaModel Type | modelType='class' | modelType='record' |
|---|---|---|
ConstrainedObjectModel | ClassRenderer | RecordRenderer |
ConstrainedEnumModel | EnumRenderer | EnumRenderer |
RenderCompleteModelOptions
1{ 2 namespace: string // Default: 'Asyncapi.Models' 3}
File Generation
1const generator = new CSharpFileGenerator({ /* options */ }); 2await generator.generateToFiles(input, './output', { 3 namespace: 'MyApp.Models' 4}); 5// Creates: ./output/ModelName.cs for each model
Preset System
CSharpPreset Hook Types
1type CSharpPreset = { 2 class?: CsharpClassPreset; // For ConstrainedObjectModel (when modelType='class') 3 record?: CsharpRecordPreset; // For ConstrainedObjectModel (when modelType='record') 4 enum?: EnumPresetType; // For ConstrainedEnumModel 5}
Class Preset Hooks
| Hook | Called | Args | Purpose |
|---|---|---|---|
self | Once per class | { renderer, model, content, options } | Override entire class output |
ctor | Once per class | { renderer, model, content, options } | Constructor |
property | Per property | { renderer, model, content, options, property } | Private field or auto-property |
accessor | Per property | { renderer, model, content, options, property } | C#-specific: Public property accessor |
getter | Per property | { renderer, model, content, options, property } | Getter method (called by accessor) |
setter | Per property | { renderer, model, content, options, property } | Setter method (called by accessor) |
additionalContent | Once per class | { renderer, model, content, options } | Extra methods |
Default class rendering:
1public partial class ModelName 2{ 3 private Type _propertyName; // property hook 4 public ModelName() { } // ctor hook 5 public Type PropertyName // accessor hook 6 { 7 get { return _propertyName; } // getter hook 8 set { _propertyName = value; } // setter hook 9 } 10 // additionalContent hook 11}
With autoImplementedProperties=true:
1public partial class ModelName 2{ 3 public Type PropertyName { get; set; } // property hook (combined) 4}
Record Preset Hooks
| Hook | Called | Args | Purpose |
|---|---|---|---|
self | Once per record | { renderer, model, content, options } | Override entire record |
property | Per property | { renderer, model, content, options, property } | Init property |
getter | Per property | { renderer, model, content, options, property } | Default: get; |
setter | Per property | { renderer, model, content, options, property } | Default: init; |
additionalContent | Once per record | { renderer, model, content, options } | Extra content |
Default record rendering:
1public partial record ModelName 2{ 3 public required Type PropertyName { get; init; } // property + getter + setter hooks 4}
Enum Preset Hooks
| Hook | Called | Args | Purpose |
|---|---|---|---|
self | Once per enum | { renderer, model, content, options } | Override entire enum + extensions |
item | Per value | { renderer, model, content, options, item } | Individual enum value |
Default enum rendering:
1public enum EnumName 2{ 3 KEY_1, // item hook 4 KEY_2 5} 6 7public static class EnumNameExtensions 8{ 9 public static Type? GetValue(this EnumName enumValue) { ... } 10 public static EnumName? ToEnumName(dynamic? value) { ... } 11}
Built-in Presets
CSHARP_COMMON_PRESET
Import: import { CSHARP_COMMON_PRESET } from '@asyncapi/modelina';
Options (CSharpCommonPresetOptions):
| Option | Type | Default | Description |
|---|---|---|---|
equal | boolean | true | Add Equals() method |
hash | boolean | true | Add GetHashCode() method |
Dependencies added: using System;
CSHARP_JSON_SERIALIZER_PRESET
Import: import { CSHARP_JSON_SERIALIZER_PRESET } from '@asyncapi/modelina';
No options required. Uses System.Text.Json.
Adds:
[JsonConverter]attribute with internal converter classSerialize()andDeserialize(string json)methods- Handles unwrapped dictionaries, enum references
- Dynamic value converter for enums
Dependencies added: System.Text.Json, System.Text.Json.Serialization, System.Text.RegularExpressions, System.Linq
CSHARP_NEWTONSOFT_SERIALIZER_PRESET
Import: import { CSHARP_NEWTONSOFT_SERIALIZER_PRESET } from '@asyncapi/modelina';
No options required. Uses Newtonsoft.Json (JSON.NET).
Adds:
[JsonConverter]attribute with converter class using JObject/JTokenSerialize()andDeserialize(string json)methods- Enforces required properties if
enforceRequired: true(throws JsonSerializationException) - Boolean enum values converted to lowercase
Dependencies added: Newtonsoft.Json, Newtonsoft.Json.Linq, System.Collections.Generic, System.Linq
Constraint System
Type Mappings
| MetaModel Type | C# Type | Notes |
|---|---|---|
| Object | ModelName or ModelName? | Nullable if not required |
| Reference | RefName or RefName? | |
| Any | dynamic or dynamic? | |
| Float | double or double? | |
| Integer | int/int? or long/long? | format:'int64' -> long |
| String | string | Default |
| String (time) | System.TimeSpan | format: 'time' |
| String (date) | System.DateTime | format: 'date' |
| String (date-time) | System.DateTimeOffset | format: 'dateTime' or 'date-time' |
| String (uuid) | System.Guid | format: 'uuid' |
| Boolean | bool or bool? | |
| Tuple | (Type1, Type2) | Value tuple syntax |
| Array (List) | IEnumerable<T> | When collectionType='List' |
| Array (Array) | T[] | When collectionType='Array' |
| Enum | EnumName or dynamic? | |
| Union | dynamic or dynamic? | |
| Dictionary | Dictionary<K, V> |
Nullability rule: Type is nullable (?) when property.required === false
Model Name Constraints
Pipeline: NO_SPECIAL_CHAR -> NO_NUMBER_START_CHAR -> NO_EMPTY_VALUE -> NO_RESERVED_KEYWORDS -> NAMING_FORMATTER(PascalCase)
Property Key Constraints
Pipeline: NO_SPECIAL_CHAR -> NO_NUMBER_START_CHAR -> NO_EMPTY_VALUE -> NO_RESERVED_KEYWORDS -> NAMING_FORMATTER(camelCase) -> NO_ENCLOSING_NAMES -> NO_DUPLICATE_PROPERTIES -> NAMING_FORMATTER(camelCase)
Note: NO_ENCLOSING_NAMES prefixes with reserved_ if property name matches class name.
Enum Key Constraints
Pipeline: NO_SPECIAL_CHAR -> NO_NUMBER_START_CHAR -> NO_EMPTY_VALUE -> NO_DUPLICATE_KEYS -> NAMING_FORMATTER(CONSTANT_CASE)
Reserved Keywords (78 total)
abstract, as, base, bool, break, byte, case, catch, char, checked, class, const, continue, decimal, default, delegate, do, double, else, enum, event, explicit, extern, false, finally, fixed, float, for, foreach, goto, if, implicit, in, int, interface, internal, is, lock, long, namespace, new, null, object, operator, out, override, params, private, protected, public, readonly, record, ref, return, sbyte, sealed, short, sizeof, stackalloc, static, string, struct, switch, this, throw, true, try, typeof, uint, ulong, unchecked, unsafe, ushort, using, virtual, void, volatile, while
Customizing Type Mappings
Override specific type mappings while keeping defaults for the rest. Each function receives a TypeContext:
constrainedModel— the constrained model needing a type stringoptions—CSharpOptionspartOfProperty?— set when resolving type for a property (nullable?added whenrequired === false)dependencyManager—CSharpDependencyManagerto add using statements
1const generator = new CSharpGenerator({ 2 typeMapping: { 3 String: ({ constrainedModel, dependencyManager }) => { 4 if (constrainedModel.options.format === 'date-time') { 5 dependencyManager.addDependency('using NodaTime;'); 6 return 'Instant'; 7 } 8 return 'string'; 9 }, 10 Float: () => 'decimal' 11 } 12});
Dependency Manager
CSharpDependencyManager extends AbstractDependencyManager with base class only.
Methods:
| Method | Description |
|---|---|
addDependency(dep: string) | Add raw using statement (deduplicates) |
Usage in presets:
1class: { 2 self({ dependencyManager, content }) { 3 dependencyManager.addDependency('using System.ComponentModel.DataAnnotations;'); 4 return content; 5 } 6}
Quick Reference Examples
Basic class generation
1const generator = new CSharpGenerator(); 2const models = await generator.generate(jsonSchema);
System.Text.Json with common methods
1const generator = new CSharpGenerator({ 2 presets: [ 3 CSHARP_JSON_SERIALIZER_PRESET, 4 { preset: CSHARP_COMMON_PRESET, options: { equal: true, hash: true } } 5 ] 6});
Record type with Newtonsoft
1const generator = new CSharpGenerator({ 2 modelType: 'record', 3 presets: [CSHARP_NEWTONSOFT_SERIALIZER_PRESET] 4});
Generate to files
1import { CSharpFileGenerator } from '@asyncapi/modelina'; 2 3const generator = new CSharpFileGenerator({ 4 presets: [CSHARP_JSON_SERIALIZER_PRESET] 5}); 6await generator.generateToFiles(schema, './generated', { 7 namespace: 'MyApp.Models' 8});