Repository: asyncapi/modelina
Pull request #735 (2022-04-27 02:09:47)

refactor: convert CSharp to new constraint setup
**Description**
This PR converts the CSharp generator to the new constraint setup introduced in https://github.com/asyncapi/modelina/pull/725

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=735)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=735) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=735&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=735)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=735) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=735&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=735)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=735) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=735&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=735)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=735&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=735) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=735&metric=duplicated_lines_density&view=list) No Duplication information



Patch:
diff --git a/src/generators/csharp/CSharpConstrainer.ts b/src/generators/csharp/CSharpConstrainer.ts
index 6eb137bb..47738b33 100644
--- a/src/generators/csharp/CSharpConstrainer.ts
+++ b/src/generators/csharp/CSharpConstrainer.ts
@@ -1,228 +1,58 @@
-import { ConstrainedAnyModel, ConstrainedBooleanModel, ConstrainedFloatModel, ConstrainedIntegerModel, ConstrainedMetaModel, ConstrainedObjectModel, ConstrainedReferenceModel, ConstrainedStringModel, ConstrainedTupleModel, ConstrainedTupleValueModel, ConstrainedArrayModel, ConstrainedUnionModel, ConstrainedEnumModel, ConstrainedDictionaryModel, ConstrainedEnumValueModel } from '../../models/ConstrainedMetaModel';
-import { AnyModel, BooleanModel, FloatModel, IntegerModel, MetaModel, ObjectModel, ReferenceModel, StringModel, TupleModel, ArrayModel, UnionModel, EnumModel, DictionaryModel } from '../../models/MetaModel';
-import { defaultPropertyKeyConstraints, PropertyKeyConstraintType } from './constrainer/PropertyKeyConstrainer';
-import { defaultModelNameConstraints, ModelNameConstraintType } from './constrainer/ModelNameConstrainer';
-import { defaultEnumKeyConstraints, EnumConstraintType } from './constrainer/EnumConstrainer';
-import { Logger } from '../../utils';
-
-export interface CSharpConstraints {
-  enumKey: EnumConstraintType,
-  modelName: ModelNameConstraintType,
-  propertyKey: PropertyKeyConstraintType,
-}
-
-export const DefaultCSharpConstraints: CSharpConstraints = {
-  enumKey: defaultEnumKeyConstraints(),
-  modelName: defaultModelNameConstraints(),
-  propertyKey: defaultPropertyKeyConstraints()
-};
-
-type TypeMappingFunction<T extends ConstrainedMetaModel> = (model: T) => string;
-
-type TypeMapping = {
-  Object?: TypeMappingFunction<ConstrainedObjectModel>,
-  Reference?: TypeMappingFunction<ConstrainedReferenceModel>,
-  Any?: TypeMappingFunction<ConstrainedAnyModel>,
-  Float?: TypeMappingFunction<ConstrainedFloatModel>,
-  Integer?: TypeMappingFunction<ConstrainedIntegerModel>,
-  String?: TypeMappingFunction<ConstrainedStringModel>,
-  Boolean?: TypeMappingFunction<ConstrainedBooleanModel>,
-  Tuple?: TypeMappingFunction<ConstrainedTupleModel>,
-  Array?: TypeMappingFunction<ConstrainedArrayModel>,
-  Enum?: TypeMappingFunction<ConstrainedEnumModel>,
-  Union?: TypeMappingFunction<ConstrainedUnionModel>,
-  Dictionary?: TypeMappingFunction<ConstrainedDictionaryModel>
-}
-
-function constrainReferenceModel(constrainedName: string, metaModel: ReferenceModel, typeMapping: TypeMapping, constrainRules: CSharpConstraints): ConstrainedReferenceModel {
-  const constrainedRefModel = constrainMetaModel(metaModel.ref, typeMapping, constrainRules);
-  const constrainedModel = new ConstrainedReferenceModel(constrainedName, metaModel.originalInput, '', constrainedRefModel);
-  if (typeMapping.Reference !== undefined) {
-    constrainedModel.type = typeMapping.Reference(constrainedModel);
-  } else {
-    constrainedModel.type = constrainedModel.name;
-  }
-  return constrainedModel;
-}
-function constrainAnyModel(constrainedName: string, metaModel: AnyModel, typeMapping: TypeMapping): ConstrainedAnyModel {
-  const constrainedModel = new ConstrainedAnyModel(constrainedName, metaModel.originalInput, '');
-  if (typeMapping.Any !== undefined) {
-    constrainedModel.type = typeMapping.Any(constrainedModel);
-  } else {
-    constrainedModel.type = 'dynamic';
-  }
-  return constrainedModel; 
-}
-function constrainFloatModel(constrainedName: string, metaModel: FloatModel, typeMapping: TypeMapping): ConstrainedFloatModel {
-  const constrainedModel = new ConstrainedFloatModel(constrainedName, metaModel.originalInput, '');
-  if (typeMapping.Float !== undefined) {
-    constrainedModel.type = typeMapping.Float(constrainedModel);
-  } else {
-    constrainedModel.type = 'float';
-  }
-  return constrainedModel;
-}
-function constrainIntegerModel(constrainedName: string, metaModel: IntegerModel, typeMapping: TypeMapping): ConstrainedIntegerModel {
-  const constrainedModel = new ConstrainedIntegerModel(constrainedName, metaModel.originalInput, '');
-  if (typeMapping.Integer !== undefined) {
-    constrainedModel.type = typeMapping.Integer(constrainedModel);
-  } else {
-    constrainedModel.type = 'int';
-  }
-  return constrainedModel;
-}
-function constrainStringModel(constrainedName: string, metaModel: IntegerModel, typeMapping: TypeMapping): ConstrainedIntegerModel {
-  const constrainedModel = new ConstrainedStringModel(constrainedName, metaModel.originalInput, '');
-  if (typeMapping.String !== undefined) {
-    constrainedModel.type = typeMapping.String(constrainedModel);
-  } else {
-    constrainedModel.type = 'string';
-  }
-  return constrainedModel;
-}
-function constrainBooleanModel(constrainedName: string, metaModel: BooleanModel, typeMapping: TypeMapping): ConstrainedBooleanModel {
-  const constrainedModel = new ConstrainedBooleanModel(constrainedName, metaModel.originalInput, '');
-  if (typeMapping.Boolean !== undefined) {
-    constrainedModel.type = typeMapping.Boolean(constrainedModel);
-  } else {
-    constrainedModel.type = 'bool';
-  }
-  return constrainedModel;
-}
-function constrainTupleModel(constrainedName: string, metaModel: TupleModel, typeMapping: TypeMapping, constrainRules: CSharpConstraints): ConstrainedTupleModel {
-  const constrainedTupleModels = metaModel.tuple.map((tupleValue) => {
-    const tupleType = constrainMetaModel(tupleValue.value, typeMapping, constrainRules);
-    return new ConstrainedTupleValueModel(tupleValue.index, tupleType);
-  });
-  const constrainedModel = new ConstrainedTupleModel(constrainedName, metaModel.originalInput, '', constrainedTupleModels);
-  if (typeMapping.Tuple !== undefined) {
-    constrainedModel.type = typeMapping.Tuple(constrainedModel);
-  } else {
-    const tupleTypes = constrainedTupleModels.map((constrainedType) => {
+import { TypeMapping } from '../../helpers';
+import { defaultEnumKeyConstraints, defaultEnumValueConstraints } from './constrainer/EnumConstrainer';
+import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer';
+import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer';
+import { CSharpRenderer } from './CSharpRenderer';
+
+export const CSharpDefaultTypeMapping: TypeMapping<CSharpRenderer> = {
+  Object ({constrainedModel}): string {
+    return constrainedModel.name;
+  },
+  Reference ({constrainedModel}): string {
+    return constrainedModel.name;
+  },
+  Any (): string {
+    return 'dynamic';
+  },
+  Float (): string {
+    return 'float';
+  },
+  Integer (): string {
+    return 'int';
+  },
+  String (): string {
+    return 'string';
+  },
+  Boolean (): string {
+    return 'bool';
+  },
+  Tuple ({constrainedModel}): string {
+    const tupleTypes = constrainedModel.tuple.map((constrainedType) => {
       return constrainedType.value.type;
     });
-    const tupleType = `(${tupleTypes.join(', ')})`;
-    constrainedModel.type = tupleType;
-  }
-  return constrainedModel;
-}
-function constrainArrayModel(constrainedName: string, metaModel: ArrayModel, typeMapping: TypeMapping, constrainRules: CSharpConstraints): ConstrainedArrayModel {
-  const constrainedValueModel = constrainMetaModel(metaModel.valueModel, typeMapping, constrainRules);
-  const constrainedModel = new ConstrainedArrayModel(constrainedName, metaModel.originalInput, '', constrainedValueModel);
-  if (typeMapping.Array !== undefined) {
-    constrainedModel.type = typeMapping.Array(constrainedModel);
-  } else {
-    constrainedModel.type = `${constrainedValueModel.type}[]`;
-  }
-  return constrainedModel;
-}
-function constrainUnionModel(constrainedName: string, metaModel: UnionModel, typeMapping: TypeMapping, constrainRules: CSharpConstraints): ConstrainedUnionModel {
-  const constrainedUnionModels = metaModel.union.map((unionValue) => {
-    return constrainMetaModel(unionValue, typeMapping, constrainRules);
-  });
-  const constrainedModel = new ConstrainedUnionModel(constrainedName, metaModel.originalInput, '', constrainedUnionModels);
-  if (typeMapping.Union !== undefined) {
-    constrainedModel.type = typeMapping.Union(constrainedModel);
-  } else {
-    constrainedModel.type = 'dynamic';
-  }
-  return constrainedModel;
-}
-function constrainDictionaryModel(constrainedName: string, metaModel: DictionaryModel, typeMapping: TypeMapping, constrainRules: CSharpConstraints): ConstrainedDictionaryModel {
-  let keyModel;    
-  //There is some restrictions on what can be used as keys for dictionaries.
-  if (metaModel.key instanceof UnionModel) {
-    Logger.error('Key for dictionary is not allowed to be union type, falling back to any model.');
-    const anyModel = new AnyModel(metaModel.key.name, metaModel.key.originalInput);
-    keyModel = constrainMetaModel(anyModel, typeMapping, constrainRules);
-  } else {
-    keyModel = constrainMetaModel(metaModel.key, typeMapping, constrainRules);
-  }
-  const valueModel = constrainMetaModel(metaModel.value, typeMapping, constrainRules);
-  const constrainedModel = new ConstrainedDictionaryModel(constrainedName, metaModel.originalInput, '', keyModel, valueModel, metaModel.serializationType);
-  if (typeMapping.Dictionary !== undefined) {
-    constrainedModel.type = typeMapping.Dictionary(constrainedModel);
-  } else {
-    const type = `Dictionary<${keyModel.type}, ${valueModel.type}>`;
-    constrainedModel.type = type;
-  }
-  return constrainedModel;
-}
-
-function constrainObjectModel(constrainedName: string, objectModel: ObjectModel, typeMapping: TypeMapping, constrainRules: CSharpConstraints): ConstrainedObjectModel {
-  const constrainedObjectModel = new ConstrainedObjectModel(constrainedName, objectModel.originalInput, '', {});
-  for (const [propertyKey, propertyMetaModel] of Object.entries(objectModel.properties)) {
-    const constrainedPropertyName = constrainRules.propertyKey({propertyKey, constrainedObjectModel, objectModel});
-    const constrainedProperty = constrainMetaModel(propertyMetaModel, typeMapping, constrainRules);
-    constrainedObjectModel.properties[String(constrainedPropertyName)] = constrainedProperty;
-  }
-  if (typeMapping.Object !== undefined) {
-    constrainedObjectModel.type = typeMapping.Object(constrainedObjectModel);
-  } else {
-    constrainedObjectModel.type = constrainedName;
-  }
-  return constrainedObjectModel;
-}
-
-export function ConstrainEnumModel(constrainedName: string, enumModel: EnumModel, typeMapping: TypeMapping, constrainRules: CSharpConstraints): ConstrainedEnumModel {
-  const constrainedModel = new ConstrainedEnumModel(constrainedName, enumModel.originalInput, '', []);
-
-  for (const enumValue of enumModel.values) {
-    const constrainedEnumKey = constrainRules.enumKey({enumKey: String(enumValue.key), enumModel, constrainedEnumModel: constrainedModel});
-    let normalizedEnumValue;
-    switch (typeof enumValue.value) {
-    case 'boolean':
-    case 'bigint':
-    case 'number': {
-      normalizedEnumValue = enumValue.value;
-      break;
-    }
-    case 'object': {
-      normalizedEnumValue = `"${JSON.stringify(enumValue).replace(/"/g, '\\"')}"`;
-      break;
-    }
-    default: {
-      normalizedEnumValue = `"${enumValue}"`;
+    return `(${tupleTypes.join(', ')})`;
+  },
+  Array ({constrainedModel, renderer}): string {
+    if (renderer.options.collectionType && renderer.options.collectionType === 'List') {
+      return `IEnumerable<${constrainedModel.valueModel.type}>`;
     }
-    }
-    const constrainedEnumValueModel = new ConstrainedEnumValueModel(constrainedEnumKey, normalizedEnumValue);
-    constrainedModel.values.push(constrainedEnumValueModel);
-  }
-  if (typeMapping.Enum !== undefined) {
-    constrainedModel.type = typeMapping.Enum(constrainedModel);
-  } else {
-    constrainedModel.type = constrainedName;
+    return `${constrainedModel.valueModel.type}[]`;
+  },
+  Enum ({constrainedModel}): string {
+    return constrainedModel.name;
+  },
+  Union (): string {
+    //Because CSharp have no notion of unions (and no custom implementation), we have to render it as any value.
+    return 'dynamic';
+  },
+  Dictionary ({constrainedModel}): string {
+    return `Dictionary<${constrainedModel.key.type}, ${constrainedModel.value.type}>`;
   }
-  return constrainedModel;
-}
+};
 
-export function constrainMetaModel(metaModel: MetaModel, typeMapping: TypeMapping, constrainRules: CSharpConstraints): ConstrainedMetaModel {
-  const constrainedName = constrainRules.modelName({modelName: metaModel.name});
-  
-  if (metaModel instanceof ObjectModel) {
-    return constrainObjectModel(constrainedName, metaModel, typeMapping, constrainRules);
-  } else if (metaModel instanceof ReferenceModel) {
-    return constrainReferenceModel(constrainedName, metaModel, typeMapping, constrainRules);
-  } else if (metaModel instanceof AnyModel) {
-    return constrainAnyModel(constrainedName, metaModel, typeMapping);
-  } else if (metaModel instanceof FloatModel) {
-    return constrainFloatModel(constrainedName, metaModel, typeMapping);
-  } else if (metaModel instanceof IntegerModel) {
-    return constrainIntegerModel(constrainedName, metaModel, typeMapping);
-  } else if (metaModel instanceof StringModel) {
-    return constrainStringModel(constrainedName, metaModel, typeMapping);
-  } else if (metaModel instanceof BooleanModel) {
-    return constrainBooleanModel(constrainedName, metaModel, typeMapping);
-  } else if (metaModel instanceof TupleModel) {
-    return constrainTupleModel(constrainedName, metaModel, typeMapping, constrainRules);
-  } else if (metaModel instanceof ArrayModel) {
-    return constrainArrayModel(constrainedName, metaModel, typeMapping, constrainRules);
-  } else if (metaModel instanceof UnionModel) {
-    return constrainUnionModel(constrainedName, metaModel, typeMapping, constrainRules);
-  } else if (metaModel instanceof EnumModel) {
-    return ConstrainEnumModel(constrainedName, metaModel, typeMapping, constrainRules);
-  } else if (metaModel instanceof DictionaryModel) {
-    return constrainDictionaryModel(constrainedName, metaModel, typeMapping, constrainRules);
-  }
-  throw new Error('Could not constrain model');
-}
+export const CSharpDefaultConstraints = {
+  enumKey: defaultEnumKeyConstraints(),
+  enumValue: defaultEnumValueConstraints(),
+  modelName: defaultModelNameConstraints(),
+  propertyKey: defaultPropertyKeyConstraints()
+};
diff --git a/src/generators/csharp/CSharpGenerator.ts b/src/generators/csharp/CSharpGenerator.ts
index bb930f49..ed97b7b2 100644
--- a/src/generators/csharp/CSharpGenerator.ts
+++ b/src/generators/csharp/CSharpGenerator.ts
@@ -4,16 +4,20 @@ import {
   defaultGeneratorOptions
 } from '../AbstractGenerator';
 import { CommonModel, CommonInputModel, RenderOutput } from '../../models';
-import { TypeHelpers, ModelKind, CommonNamingConvention, CommonNamingConventionImplementation, FormatHelpers } from '../../helpers';
+import { TypeHelpers, ModelKind, CommonNamingConvention, CommonNamingConventionImplementation, FormatHelpers, TypeMapping, Constraints } from '../../helpers';
 import { CSharpPreset, CSHARP_DEFAULT_PRESET } from './CSharpPreset';
 import { EnumRenderer } from './renderers/EnumRenderer';
 import { ClassRenderer } from './renderers/ClassRenderer';
 import { isReservedCSharpKeyword } from './Constants';
 import { Logger } from '../../index';
+import { CSharpRenderer } from './CSharpRenderer';
+import { CSharpDefaultConstraints, CSharpDefaultTypeMapping } from './CSharpConstrainer';
 
 export interface CSharpOptions extends CommonGeneratorOptions<CSharpPreset> {
   collectionType?: 'List' | 'Array';
   namingConvention?: CommonNamingConvention;
+  typeMapping: TypeMapping<CSharpRenderer>;
+  constraints: Constraints;
 }
 
 export interface CSharpRenderCompleteModelOptions {
@@ -28,13 +32,17 @@ export class CSharpGenerator extends AbstractGenerator<CSharpOptions, CSharpRend
     ...defaultGeneratorOptions,
     collectionType: 'Array',
     defaultPreset: CSHARP_DEFAULT_PRESET,
-    namingConvention: CommonNamingConventionImplementation
+    namingConvention: CommonNamingConventionImplementation,
+    typeMapping: CSharpDefaultTypeMapping,
+    constraints: CSharpDefaultConstraints
   };
 
   constructor(
-    options: CSharpOptions = CSharpGenerator.defaultOptions,
+    options: Partial<CSharpOptions> = CSharpGenerator.defaultOptions,
   ) {
-    super('CSharp', CSharpGenerator.defaultOptions, options);
+    const mergedOptions = {...CSharpGenerator.defaultOptions, ...options};
+
+    super('CSharp', CSharpGenerator.defaultOptions, mergedOptions);
   }
 
   /**
diff --git a/src/generators/csharp/constrainer/EnumConstrainer.ts b/src/generators/csharp/constrainer/EnumConstrainer.ts
index 53f08f4d..ee305766 100644
--- a/src/generators/csharp/constrainer/EnumConstrainer.ts
+++ b/src/generators/csharp/constrainer/EnumConstrainer.ts
@@ -1,7 +1,7 @@
 /* eslint-disable @typescript-eslint/no-non-null-assertion */
 import { ConstrainedEnumModel, EnumModel } from '../../../models';
 import { NO_NUMBER_START_CHAR, NO_DUPLICATE_ENUM_KEYS, NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS} from '../../../helpers/Constraints';
-import { FormatHelpers } from '../../../helpers';
+import { EnumKeyConstraint, EnumValueConstraint, FormatHelpers } from '../../../helpers';
 import { isReservedCSharpKeyword } from '../Constants';
 
 export type ModelEnumKeyConstraints = {
@@ -27,40 +27,39 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = {
   }
 };
 
-export type EnumKeyContext = {
-  enumKey: string,
-  constrainedEnumModel: ConstrainedEnumModel,
-  enumModel: EnumModel
+export function defaultEnumKeyConstraints(customConstraints?: Partial<ModelEnumKeyConstraints>): EnumKeyConstraint {
+  const constraints = {...DefaultEnumKeyConstraints, ...customConstraints};
+
+  return ({enumKey, enumModel, constrainedEnumModel}) => {
+    let constrainedEnumKey = enumKey;
+    constrainedEnumKey = constraints.NO_SPECIAL_CHAR(constrainedEnumKey);
+    constrainedEnumKey = constraints.NO_NUMBER_START_CHAR(constrainedEnumKey);
+    constrainedEnumKey = constraints.NO_EMPTY_VALUE(constrainedEnumKey);
+    constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS(constrainedEnumKey);
+    constrainedEnumKey = constraints.NAMING_FORMATTER(constrainedEnumKey);
+    constrainedEnumKey = constraints.NO_DUPLICATE_KEYS(constrainedEnumModel, enumModel, constrainedEnumKey, constraints.NAMING_FORMATTER!);
+    return constrainedEnumKey;
+  };
 }
-export type EnumConstraintType = (context: EnumKeyContext, constraints?: ModelEnumKeyConstraints) => string;
 
-export function defaultEnumKeyConstraints(customConstraints?: ModelEnumKeyConstraints): EnumConstraintType {
-  const constraints = DefaultEnumKeyConstraints;
-  if (customConstraints !== undefined) {
-    if (customConstraints.NAMING_FORMATTER !== undefined) {
-      constraints.NAMING_FORMATTER = customConstraints.NAMING_FORMATTER;
+export function defaultEnumValueConstraints(): EnumValueConstraint {
+  return ({enumValue}) => {
+    let normalizedEnumValue;
+    switch (typeof enumValue.value) {
+    case 'boolean':
+    case 'bigint':
+    case 'number': {
+      normalizedEnumValue = enumValue.value;
+      break;
     }
-    if (customConstraints.NO_SPECIAL_CHAR !== undefined) {
-      constraints.NO_SPECIAL_CHAR = customConstraints.NO_SPECIAL_CHAR;
+    case 'object': {
+      normalizedEnumValue = `"${JSON.stringify(enumValue).replace(/"/g, '\\"')}"`;
+      break;
     }
-    if (customConstraints.NO_NUMBER_START_CHAR !== undefined) {
-      constraints.NO_NUMBER_START_CHAR = customConstraints.NO_NUMBER_START_CHAR;
+    default: {
+      normalizedEnumValue = `"${enumValue}"`;
     }
-    if (customConstraints.NO_RESERVED_KEYWORDS !== undefined) {
-      constraints.NO_RESERVED_KEYWORDS = customConstraints.NO_RESERVED_KEYWORDS;
     }
-    if (customConstraints.NO_DUPLICATE_KEYS !== undefined) {
-      constraints.NO_DUPLICATE_KEYS = customConstraints.NO_DUPLICATE_KEYS;
-    }
-  }
-  return ({enumKey, enumModel, constrainedEnumModel}) => {
-    let constrainedEnumKey = enumKey;
-    constrainedEnumKey = constraints.NO_SPECIAL_CHAR!(constrainedEnumKey);
-    constrainedEnumKey = constraints.NO_NUMBER_START_CHAR!(constrainedEnumKey);
-    constrainedEnumKey = constraints.NO_EMPTY_VALUE!(constrainedEnumKey);
-    constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS!(constrainedEnumKey);
-    constrainedEnumKey = constraints.NAMING_FORMATTER!(constrainedEnumKey);
-    constrainedEnumKey = constraints.NO_DUPLICATE_KEYS!(constrainedEnumModel, enumModel, constrainedEnumKey, constraints.NAMING_FORMATTER!);
-    return constrainedEnumKey;
+    return normalizedEnumValue;
   };
 }
diff --git a/src/generators/csharp/constrainer/ModelNameConstrainer.ts b/src/generators/csharp/constrainer/ModelNameConstrainer.ts
index 4434330a..f2a73fa4 100644
--- a/src/generators/csharp/constrainer/ModelNameConstrainer.ts
+++ b/src/generators/csharp/constrainer/ModelNameConstrainer.ts
@@ -1,14 +1,14 @@
 /* eslint-disable @typescript-eslint/no-non-null-assertion */
 import { NO_NUMBER_START_CHAR, NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS} from '../../../helpers/Constraints';
-import { FormatHelpers } from '../../../helpers';
+import { FormatHelpers, ModelNameConstraint } from '../../../helpers';
 import { isReservedCSharpKeyword } from '../Constants';
 
 export type ModelNameConstraints = {
-  NO_SPECIAL_CHAR?: (value: string) => string;
-  NO_NUMBER_START_CHAR?: (value: string) => string;
-  NO_EMPTY_VALUE?: (value: string) => string;
-  NAMING_FORMATTER?: (value: string) => string;
-  NO_RESERVED_KEYWORDS?: (value: string) => string;
+  NO_SPECIAL_CHAR: (value: string) => string;
+  NO_NUMBER_START_CHAR: (value: string) => string;
+  NO_EMPTY_VALUE: (value: string) => string;
+  NAMING_FORMATTER: (value: string) => string;
+  NO_RESERVED_KEYWORDS: (value: string) => string;
 };
 
 export const DefaultModelNameConstraints: ModelNameConstraints = {
@@ -26,38 +26,16 @@ export const DefaultModelNameConstraints: ModelNameConstraints = {
     return NO_RESERVED_KEYWORDS(value, isReservedCSharpKeyword); 
   }
 };
-export type ModelNameContext = {
-  modelName: string
-}
-export type ModelNameConstraintType = (context: ModelNameContext) => string;
-
-export function defaultModelNameConstraints(customConstraints?: ModelNameConstraints): ModelNameConstraintType {
-  const constraints = DefaultModelNameConstraints;
-  if (customConstraints !== undefined) {
-    if (customConstraints.NO_SPECIAL_CHAR !== undefined) {
-      constraints.NO_SPECIAL_CHAR = customConstraints.NO_SPECIAL_CHAR;
-    }
-    if (customConstraints.NO_NUMBER_START_CHAR !== undefined) {
-      constraints.NO_NUMBER_START_CHAR = customConstraints.NO_NUMBER_START_CHAR;
-    }
-    if (customConstraints.NO_EMPTY_VALUE !== undefined) {
-      constraints.NO_EMPTY_VALUE = customConstraints.NO_EMPTY_VALUE;
-    }
-    if (customConstraints.NAMING_FORMATTER !== undefined) {
-      constraints.NAMING_FORMATTER = customConstraints.NAMING_FORMATTER;
-    }
-    if (customConstraints.NO_RESERVED_KEYWORDS !== undefined) {
-      constraints.NAMING_FORMATTER = customConstraints.NO_RESERVED_KEYWORDS;
-    }
-  }
+export function defaultModelNameConstraints(customConstraints?: Partial<ModelNameConstraints>): ModelNameConstraint {
+  const constraints = {...DefaultModelNameConstraints, ...customConstraints};
 
   return ({modelName}) => {
     let constrainedValue = modelName;
-    constrainedValue = constraints.NO_SPECIAL_CHAR!(constrainedValue);
-    constrainedValue = constraints.NO_NUMBER_START_CHAR!(constrainedValue);
-    constrainedValue = constraints.NO_EMPTY_VALUE!(constrainedValue);
-    constrainedValue = constraints.NAMING_FORMATTER!(constrainedValue);
-    constrainedValue = constraints.NO_RESERVED_KEYWORDS!(constrainedValue);
+    constrainedValue = constraints.NO_SPECIAL_CHAR(constrainedValue);
+    constrainedValue = constraints.NO_NUMBER_START_CHAR(constrainedValue);
+    constrainedValue = constraints.NO_EMPTY_VALUE(constrainedValue);
+    constrainedValue = constraints.NO_RESERVED_KEYWORDS(constrainedValue);
+    constrainedValue = constraints.NAMING_FORMATTER(constrainedValue);
     return constrainedValue;
   };
 }
diff --git a/src/generators/csharp/constrainer/PropertyKeyConstrainer.ts b/src/generators/csharp/constrainer/PropertyKeyConstrainer.ts
index 1af3387e..a2a324ce 100644
--- a/src/generators/csharp/constrainer/PropertyKeyConstrainer.ts
+++ b/src/generators/csharp/constrainer/PropertyKeyConstrainer.ts
@@ -1,16 +1,15 @@
-/* eslint-disable @typescript-eslint/no-non-null-assertion */
 import { ConstrainedObjectModel, ObjectModel } from '../../../models';
 import { NO_NUMBER_START_CHAR, NO_DUPLICATE_PROPERTIES, NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS} from '../../../helpers/Constraints';
-import { FormatHelpers } from '../../../helpers';
+import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers';
 import { isReservedCSharpKeyword } from '../Constants';
 
 export type PropertyKeyConstraintOptions = {
-  NO_SPECIAL_CHAR?: (value: string) => string;
-  NO_NUMBER_START_CHAR?: (value: string) => string;
-  NO_DUPLICATE_PROPERTIES?: (constrainedObjectModel: ConstrainedObjectModel, objectModel: ObjectModel, propertyName: string, namingFormatter: (value: string) => string) => string;
-  NO_EMPTY_VALUE?: (value: string) => string;
-  NAMING_FORMATTER?: (value: string) => string;
-  NO_RESERVED_KEYWORDS?: (value: string) => string;
+  NO_SPECIAL_CHAR: (value: string) => string;
+  NO_NUMBER_START_CHAR: (value: string) => string;
+  NO_DUPLICATE_PROPERTIES: (constrainedObjectModel: ConstrainedObjectModel, objectModel: ObjectModel, propertyName: string, namingFormatter: (value: string) => string) => string;
+  NO_EMPTY_VALUE: (value: string) => string;
+  NAMING_FORMATTER: (value: string) => string;
+  NO_RESERVED_KEYWORDS: (value: string) => string;
 };
 
 export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = {
@@ -28,45 +27,18 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = {
   }
 };
 
-export type PropertyKeyContext = {
-  propertyKey: string,
-  constrainedObjectModel: ConstrainedObjectModel,
-  objectModel: ObjectModel
-}
-
-export type PropertyKeyConstraintType = (context: PropertyKeyContext) => string;
+export function defaultPropertyKeyConstraints(customConstraints?: Partial<PropertyKeyConstraintOptions>): PropertyKeyConstraint {
+  const constraints = {...DefaultPropertyKeyConstraints, ...customConstraints};
 
-export function defaultPropertyKeyConstraints(customConstraints?: PropertyKeyConstraintOptions): PropertyKeyConstraintType {
-  const constraints = DefaultPropertyKeyConstraints;
-  if (customConstraints !== undefined) {
-    if (customConstraints.NAMING_FORMATTER !== undefined) {
-      constraints.NAMING_FORMATTER = customConstraints.NAMING_FORMATTER;
-    }
-    if (customConstraints.NO_SPECIAL_CHAR !== undefined) {
-      constraints.NO_SPECIAL_CHAR = customConstraints.NO_SPECIAL_CHAR;
-    }
-    if (customConstraints.NO_NUMBER_START_CHAR !== undefined) {
-      constraints.NO_NUMBER_START_CHAR = customConstraints.NO_NUMBER_START_CHAR;
-    }
-    if (customConstraints.NO_EMPTY_VALUE !== undefined) {
-      constraints.NO_EMPTY_VALUE = customConstraints.NO_EMPTY_VALUE;
-    }
-    if (customConstraints.NO_RESERVED_KEYWORDS !== undefined) {
-      constraints.NO_RESERVED_KEYWORDS = customConstraints.NO_RESERVED_KEYWORDS;
-    }
-    if (customConstraints.NO_DUPLICATE_PROPERTIES !== undefined) {
-      constraints.NO_DUPLICATE_PROPERTIES = customConstraints.NO_DUPLICATE_PROPERTIES;
-    }
-  }
-  return ({propertyKey, constrainedObjectModel, objectModel}: PropertyKeyContext) => {
+  return ({propertyKey, constrainedObjectModel, objectModel}) => {
     let constrainedPropertyKey = propertyKey;
 
-    constrainedPropertyKey = constraints.NO_SPECIAL_CHAR!(constrainedPropertyKey);
-    constrainedPropertyKey = constraints.NO_NUMBER_START_CHAR!(constrainedPropertyKey);
-    constrainedPropertyKey = constraints.NO_EMPTY_VALUE!(constrainedPropertyKey);
-    constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS!(constrainedPropertyKey);
-    constrainedPropertyKey = constraints.NAMING_FORMATTER!(constrainedPropertyKey);
-    constrainedPropertyKey = constraints.NO_DUPLICATE_PROPERTIES!(constrainedObjectModel, objectModel, constrainedPropertyKey, constraints.NAMING_FORMATTER!);
+    constrainedPropertyKey = constraints.NO_SPECIAL_CHAR(constrainedPropertyKey);
+    constrainedPropertyKey = constraints.NO_NUMBER_START_CHAR(constrainedPropertyKey);
+    constrainedPropertyKey = constraints.NO_EMPTY_VALUE(constrainedPropertyKey);
+    constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS(constrainedPropertyKey);
+    constrainedPropertyKey = constraints.NAMING_FORMATTER(constrainedPropertyKey);
+    constrainedPropertyKey = constraints.NO_DUPLICATE_PROPERTIES(constrainedObjectModel, objectModel, constrainedPropertyKey, constraints.NAMING_FORMATTER);
     return constrainedPropertyKey;
   };
 }
diff --git a/src/generators/java/JavaGenerator.ts b/src/generators/java/JavaGenerator.ts
index 873c3b4e..40385785 100644
--- a/src/generators/java/JavaGenerator.ts
+++ b/src/generators/java/JavaGenerator.ts
@@ -18,7 +18,7 @@ export interface JavaOptions extends CommonGeneratorOptions<JavaPreset> {
   collectionType: 'List' | 'Array';
   namingConvention: CommonNamingConvention;
   typeMapping: TypeMapping<JavaRenderer>;
-  constraints: Constraints
+  constraints: Constraints;
 }
 export interface JavaRenderCompleteModelOptions {
   packageName: string


Test patch:
diff --git a/test/TestUtils/TestRenderers.ts b/test/TestUtils/TestRenderers.ts
index 311087cf..590e6f17 100644
--- a/test/TestUtils/TestRenderers.ts
+++ b/test/TestUtils/TestRenderers.ts
@@ -1,4 +1,5 @@
 import { AbstractRenderer, CommonInputModel, CommonModel, RenderOutput } from '../../src';
+import { CSharpRenderer } from '../../src/generators/csharp/CSharpRenderer';
 import { JavaRenderer } from '../../src/generators/java/JavaRenderer';
 import {testOptions, TestGenerator} from './TestGenerator';
 
@@ -11,6 +12,5 @@ export class TestRenderer extends AbstractRenderer {
   }
 }
 
-export class MockJavaRenderer extends JavaRenderer {
-
-}
+export class MockJavaRenderer extends JavaRenderer {}
+export class MockCSharpRenderer extends CSharpRenderer {}
diff --git a/test/generators/csharp/CSharpConstrainer.spec.ts b/test/generators/csharp/CSharpConstrainer.spec.ts
new file mode 100644
index 00000000..11f45962
--- /dev/null
+++ b/test/generators/csharp/CSharpConstrainer.spec.ts
@@ -0,0 +1,121 @@
+import { CSharpDefaultTypeMapping } from '../../../src/generators/csharp/CSharpConstrainer';
+import { MockCSharpRenderer } from '../../TestUtils/TestRenderers';
+import { CSharpRenderer } from '../../../src/generators/csharp/CSharpRenderer';
+import { CommonInputModel, CommonModel, ConstrainedAnyModel, ConstrainedArrayModel, ConstrainedBooleanModel, ConstrainedDictionaryModel, ConstrainedEnumModel, ConstrainedFloatModel, ConstrainedIntegerModel, ConstrainedObjectModel, ConstrainedReferenceModel, ConstrainedStringModel, ConstrainedTupleModel, ConstrainedTupleValueModel, ConstrainedUnionModel, CSharpGenerator } from '../../../src';
+describe('CSharpConstrainer', () => {
+  let renderer: CSharpRenderer;
+  beforeEach(() => {
+    renderer = new MockCSharpRenderer(CSharpGenerator.defaultOptions, new CSharpGenerator(), [], new CommonModel(), new CommonInputModel());
+  });
+  describe('ObjectModel', () => { 
+    test('should render the constrained name as type', () => {
+      const model = new ConstrainedObjectModel('test', undefined, '', {});
+      const type = CSharpDefaultTypeMapping.Object({constrainedModel: model, renderer});
+      expect(type).toEqual(model.name);
+    });
+  });
+  describe('Reference', () => { 
+    test('should render the constrained name as type', () => {
+      const refModel = new ConstrainedAnyModel('test', undefined, '');
+      const model = new ConstrainedReferenceModel('test', undefined, '', refModel);
+      const type = CSharpDefaultTypeMapping.Reference({constrainedModel: model, renderer});
+      expect(type).toEqual(model.name);
+    });
+  });
+  describe('Any', () => { 
+    test('should render type', () => {
+      const model = new ConstrainedAnyModel('test', undefined, '');
+      const type = CSharpDefaultTypeMapping.Any({constrainedModel: model, renderer});
+      expect(type).toEqual('dynamic');
+    });
+  });
+  describe('Float', () => { 
+    test('should render type', () => {
+      const model = new ConstrainedFloatModel('test', undefined, '');
+      const type = CSharpDefaultTypeMapping.Float({constrainedModel: model, renderer});
+      expect(type).toEqual('float');
+    });
+  });
+  describe('Integer', () => { 
+    test('should render type', () => {
+      const model = new ConstrainedIntegerModel('test', undefined, '');
+      const type = CSharpDefaultTypeMapping.Integer({constrainedModel: model, renderer});
+      expect(type).toEqual('int');
+    });
+  });
+  describe('String', () => { 
+    test('should render type', () => {
+      const model = new ConstrainedStringModel('test', undefined, '');
+      const type = CSharpDefaultTypeMapping.String({constrainedModel: model, renderer});
+      expect(type).toEqual('string');
+    });
+  });
+  describe('Boolean', () => { 
+    test('should render type', () => {
+      const model = new ConstrainedBooleanModel('test', undefined, '');
+      const type = CSharpDefaultTypeMapping.Boolean({constrainedModel: model, renderer});
+      expect(type).toEqual('bool');
+    });
+  });
+
+  describe('Tuple', () => { 
+    test('should render type', () => {
+      const tupleModel = new ConstrainedBooleanModel('test', undefined, 'string');
+      const tupleValueModel = new ConstrainedTupleValueModel(0, tupleModel);
+      const model = new ConstrainedTupleModel('test', undefined, '', [tupleValueModel]);
+      const type = CSharpDefaultTypeMapping.Tuple({constrainedModel: model, renderer});
+      expect(type).toEqual('(string)');
+    });
+    test('should render multiple types', () => {
+      const tupleModel = new ConstrainedBooleanModel('test', undefined, 'string');
+      const tupleValueModel0 = new ConstrainedTupleValueModel(0, tupleModel);
+      const tupleValueModel1 = new ConstrainedTupleValueModel(1, tupleModel);
+      const model = new ConstrainedTupleModel('test', undefined, '', [tupleValueModel0, tupleValueModel1]);
+      const type = CSharpDefaultTypeMapping.Tuple({constrainedModel: model, renderer});
+      expect(type).toEqual('(string, string)');
+    });
+  });
+
+  describe('Array', () => { 
+    test('should render type', () => {
+      const arrayModel = new ConstrainedStringModel('test', undefined, 'String');
+      const model = new ConstrainedArrayModel('test', undefined, '', arrayModel);
+      renderer.options.collectionType = 'Array';
+      const type = CSharpDefaultTypeMapping.Array({constrainedModel: model, renderer});
+      expect(type).toEqual('String[]');
+    });
+    test('should render array as a list', () => {
+      const arrayModel = new ConstrainedStringModel('test', undefined, 'String');
+      const model = new ConstrainedArrayModel('test', undefined, '', arrayModel);
+      renderer.options.collectionType = 'List';
+      const type = CSharpDefaultTypeMapping.Array({constrainedModel: model, renderer});
+      expect(type).toEqual('IEnumerable<String>');
+    });
+  });
+
+  describe('Enum', () => { 
+    test('should render the constrained name as type', () => {
+      const model = new ConstrainedEnumModel('test', undefined, '', []);
+      const type = CSharpDefaultTypeMapping.Enum({constrainedModel: model, renderer});
+      expect(type).toEqual(model.name);
+    });
+  });
+
+  describe('Union', () => { 
+    test('should render type', () => {
+      const model = new ConstrainedUnionModel('test', undefined, '', []);
+      const type = CSharpDefaultTypeMapping.Union({constrainedModel: model, renderer});
+      expect(type).toEqual('dynamic');
+    });
+  });
+
+  describe('Dictionary', () => { 
+    test('should render type', () => {
+      const keyModel = new ConstrainedStringModel('test', undefined, 'String');
+      const valueModel = new ConstrainedStringModel('test', undefined, 'String');
+      const model = new ConstrainedDictionaryModel('test', undefined, '', keyModel, valueModel);
+      const type = CSharpDefaultTypeMapping.Dictionary({constrainedModel: model, renderer});
+      expect(type).toEqual('Dictionary<String, String>');
+    });
+  });
+});
diff --git a/test/generators/csharp/constrainer/EnumConstrainer.spec.ts b/test/generators/csharp/constrainer/EnumConstrainer.spec.ts
new file mode 100644
index 00000000..3b36eab9
--- /dev/null
+++ b/test/generators/csharp/constrainer/EnumConstrainer.spec.ts
@@ -0,0 +1,94 @@
+import { CSharpDefaultConstraints } from '../../../../src/generators/csharp/CSharpConstrainer';
+import {EnumModel} from '../../../../src/models/MetaModel';
+import { ConstrainedEnumModel, ConstrainedEnumValueModel } from '../../../../src';
+import { defaultEnumKeyConstraints, ModelEnumKeyConstraints, DefaultEnumKeyConstraints } from '../../../../src/generators/csharp/constrainer/EnumConstrainer';
+describe('EnumConstrainer', () => {
+  const enumModel = new EnumModel('test', undefined, []);
+  const constrainedEnumModel = new ConstrainedEnumModel('test', undefined, '', []);
+
+  describe('enum keys', () => {
+    test('should never render special chars', () => {
+      const constrainedKey = CSharpDefaultConstraints.enumKey({enumModel, constrainedEnumModel, enumKey: '%'});
+      expect(constrainedKey).toEqual('PERCENT');
+    });
+    test('should not render number as start char', () => {
+      const constrainedKey = CSharpDefaultConstraints.enumKey({enumModel, constrainedEnumModel, enumKey: '1'});
+      expect(constrainedKey).toEqual('NUMBER_1');
+    });
+    test('should not contain duplicate keys', () => {
+      const existingConstrainedEnumValueModel = new ConstrainedEnumValueModel('TEST', 'test');
+      const constrainedEnumModel = new ConstrainedEnumModel('test', undefined, '', [existingConstrainedEnumValueModel]);
+      const constrainedKey = CSharpDefaultConstraints.enumKey({enumModel, constrainedEnumModel, enumKey: 'TEST'});
+      expect(constrainedKey).toEqual('RESERVED_TEST');
+    });
+    test('should never contain empty keys', () => {
+      const constrainedKey = CSharpDefaultConstraints.enumKey({enumModel, constrainedEnumModel, enumKey: ''});
+      expect(constrainedKey).toEqual('EMPTY');
+    });
+    test('should use constant naming format', () => {
+      const constrainedKey = CSharpDefaultConstraints.enumKey({enumModel, constrainedEnumModel, enumKey: 'some weird_value!"#2'});
+      expect(constrainedKey).toEqual('SOME_SPACE_WEIRD_VALUE_EXCLAMATION_QUOTATION_HASH_2');
+    });
+    test('should never render reserved keywords', () => {
+      const constrainedKey = CSharpDefaultConstraints.enumKey({enumModel, constrainedEnumModel, enumKey: 'return'});
+      expect(constrainedKey).toEqual('RESERVED_RETURN');
+    });
+  });
+  describe('enum values', () => {
+    test('should render string values', () => {
+      const constrainedValue = CSharpDefaultConstraints.enumValue({enumModel, constrainedEnumModel, enumValue: 'string value'});
+      expect(constrainedValue).toEqual('"string value"');
+    });
+    test('should render boolean values', () => {
+      const constrainedValue = CSharpDefaultConstraints.enumValue({enumModel, constrainedEnumModel, enumValue: true});
+      expect(constrainedValue).toEqual('"true"');
+    });
+    test('should render numbers', () => {
+      const constrainedValue = CSharpDefaultConstraints.enumValue({enumModel, constrainedEnumModel, enumValue: 123});
+      expect(constrainedValue).toEqual('"123"');
+    });
+    test.skip('should render object', () => {
+      const constrainedValue = CSharpDefaultConstraints.enumValue({enumModel, constrainedEnumModel, enumValue: {test: 'test'}});
+      expect(constrainedValue).toEqual('"{\\"test\\":\\"test\\"}"');
+    });
+    test.skip('should render unknown value', () => {
+      const constrainedValue = CSharpDefaultConstraints.enumValue({enumModel, constrainedEnumModel, enumValue: undefined});
+      expect(constrainedValue).toEqual('"undefined"');
+    });
+  });
+  describe('custom constraints', () => {
+    test('should be able to overwrite all hooks for enum key', () => {
+      const mockedConstraintCallbacks: ModelEnumKeyConstraints = {
+        NAMING_FORMATTER: jest.fn().mockReturnValue(''),
+        NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''),
+        NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''),
+        NO_DUPLICATE_KEYS: jest.fn().mockReturnValue(''),
+        NO_EMPTY_VALUE: jest.fn().mockReturnValue(''),
+        NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('')
+      };
+      const constrainFunction = defaultEnumKeyConstraints(mockedConstraintCallbacks);
+      constrainFunction({enumModel, constrainedEnumModel, enumKey: ''});
+      //Expect all callbacks to be called
+      for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) {
+        expect(jestMockCallback).toHaveBeenCalled();
+      }
+    });
+    test('should be able to overwrite one hooks for enum key', () => {
+      //All but NAMING_FORMATTER, as we customize that
+      const spies = [
+        jest.spyOn(DefaultEnumKeyConstraints, 'NO_SPECIAL_CHAR'),
+        jest.spyOn(DefaultEnumKeyConstraints, 'NO_NUMBER_START_CHAR'),
+        jest.spyOn(DefaultEnumKeyConstraints, 'NO_DUPLICATE_KEYS'),
+        jest.spyOn(DefaultEnumKeyConstraints, 'NO_EMPTY_VALUE'),
+        jest.spyOn(DefaultEnumKeyConstraints, 'NO_RESERVED_KEYWORDS')
+      ];
+      const jestCallback = jest.fn().mockReturnValue('');
+      const constrainFunction = defaultEnumKeyConstraints({NAMING_FORMATTER: jestCallback});
+      const constrainedValue = constrainFunction({enumModel, constrainedEnumModel, enumKey: ''});
+      expect(constrainedValue).toEqual('');
+      for (const jestMockCallback of spies) {
+        expect(jestMockCallback).toHaveBeenCalled();
+      }
+    });
+  });
+});
diff --git a/test/generators/csharp/constrainer/ModelNameConstrainer.spec.ts b/test/generators/csharp/constrainer/ModelNameConstrainer.spec.ts
new file mode 100644
index 00000000..5b13d09f
--- /dev/null
+++ b/test/generators/csharp/constrainer/ModelNameConstrainer.spec.ts
@@ -0,0 +1,57 @@
+import { CSharpDefaultConstraints } from '../../../../src/generators/csharp/CSharpConstrainer';
+import { DefaultModelNameConstraints, defaultModelNameConstraints, ModelNameConstraints } from '../../../../src/generators/csharp/constrainer/ModelNameConstrainer';
+describe('ModelNameConstrainer', () => {
+  test('should never render special chars', () => {
+    const constrainedKey = CSharpDefaultConstraints.modelName({modelName: '%'});
+    expect(constrainedKey).toEqual('Percent');
+  });
+  test('should never render number as start char', () => {
+    const constrainedKey = CSharpDefaultConstraints.modelName({modelName: '1'});
+    expect(constrainedKey).toEqual('Number_1');
+  });
+  test('should never contain empty name', () => {
+    const constrainedKey = CSharpDefaultConstraints.modelName({modelName: ''});
+    expect(constrainedKey).toEqual('Empty');
+  });
+  test('should use constant naming format', () => {
+    const constrainedKey = CSharpDefaultConstraints.modelName({modelName: 'some weird_value!"#2'});
+    expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2');
+  });
+  test('should never render reserved keywords', () => {
+    const constrainedKey = CSharpDefaultConstraints.modelName({modelName: 'return'});
+    expect(constrainedKey).toEqual('ReservedReturn');
+  });
+  describe('custom constraints', () => {
+    test('should be able to overwrite all hooks', () => {
+      const mockedConstraintCallbacks: ModelNameConstraints = {
+        NAMING_FORMATTER: jest.fn().mockReturnValue(''),
+        NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''),
+        NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''),
+        NO_EMPTY_VALUE: jest.fn().mockReturnValue(''),
+        NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('')
+      };
+      const constrainFunction = defaultModelNameConstraints(mockedConstraintCallbacks);
+      constrainFunction({modelName: ''});
+      //Expect all callbacks to be called
+      for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) {
+        expect(jestMockCallback).toHaveBeenCalled();
+      }
+    });
+    test('should be able to overwrite one hooks', () => {
+      //All but NAMING_FORMATTER, as we customize that
+      const spies = [
+        jest.spyOn(DefaultModelNameConstraints, 'NO_SPECIAL_CHAR'),
+        jest.spyOn(DefaultModelNameConstraints, 'NO_NUMBER_START_CHAR'),
+        jest.spyOn(DefaultModelNameConstraints, 'NO_EMPTY_VALUE'),
+        jest.spyOn(DefaultModelNameConstraints, 'NO_RESERVED_KEYWORDS')
+      ];
+      const jestCallback = jest.fn().mockReturnValue('');
+      const constrainFunction = defaultModelNameConstraints({NAMING_FORMATTER: jestCallback});
+      const constrainedValue = constrainFunction({modelName: ''});
+      expect(constrainedValue).toEqual('');
+      for (const jestMockCallback of spies) {
+        expect(jestMockCallback).toHaveBeenCalled();
+      }
+    });
+  });
+});
diff --git a/test/generators/csharp/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/csharp/constrainer/PropertyKeyConstrainer.spec.ts
new file mode 100644
index 00000000..90350e6e
--- /dev/null
+++ b/test/generators/csharp/constrainer/PropertyKeyConstrainer.spec.ts
@@ -0,0 +1,70 @@
+import { CSharpDefaultConstraints } from '../../../../src/generators/csharp/CSharpConstrainer';
+import { ConstrainedObjectModel, ObjectModel } from '../../../../src';
+import { DefaultPropertyKeyConstraints, defaultPropertyKeyConstraints, PropertyKeyConstraintOptions } from '../../../../src/generators/csharp/constrainer/PropertyKeyConstrainer';
+describe('PropertyKeyConstrainer', () => {
+  const objectModel = new ObjectModel('test', undefined, {});
+  const constrainedObjectModel = new ConstrainedObjectModel('test', undefined, '', {});
+
+  test('should never render special chars', () => {
+    const constrainedKey = CSharpDefaultConstraints.propertyKey({constrainedObjectModel, objectModel, propertyKey: '%'});
+    expect(constrainedKey).toEqual('Percent');
+  });
+  test('should not render number as start char', () => {
+    const constrainedKey = CSharpDefaultConstraints.propertyKey({constrainedObjectModel, objectModel, propertyKey: '1'});
+    expect(constrainedKey).toEqual('Number_1');
+  });
+  test('should never contain empty name', () => {
+    const constrainedKey = CSharpDefaultConstraints.propertyKey({constrainedObjectModel, objectModel, propertyKey: ''});
+    expect(constrainedKey).toEqual('Empty');
+  });
+  test('should use constant naming format', () => {
+    const constrainedKey = CSharpDefaultConstraints.propertyKey({constrainedObjectModel, objectModel, propertyKey: 'some weird_value!"#2'});
+    expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2');
+  });
+  test('should not contain duplicate properties', () => {
+    const objectModel = new ObjectModel('test', undefined, {});
+    const propertyModel = new ConstrainedObjectModel('SomeProperty', undefined, '', {});
+    const constrainedObjectModel = new ConstrainedObjectModel('test', undefined, '', {SomeProperty: propertyModel});
+    const constrainedKey = CSharpDefaultConstraints.propertyKey({constrainedObjectModel, objectModel, propertyKey: 'SomeProperty'});
+    expect(constrainedKey).toEqual('ReservedSomeProperty');
+  });
+  test('should never render reserved keywords', () => {
+    const constrainedKey = CSharpDefaultConstraints.propertyKey({constrainedObjectModel, objectModel, propertyKey: 'return'});
+    expect(constrainedKey).toEqual('ReservedReturn');
+  });
+  describe('custom constraints', () => {
+    test('should be able to overwrite all hooks', () => {
+      const mockedConstraintCallbacks: PropertyKeyConstraintOptions = {
+        NAMING_FORMATTER: jest.fn().mockReturnValue(''),
+        NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''),
+        NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''),
+        NO_EMPTY_VALUE: jest.fn().mockReturnValue(''),
+        NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue(''),
+        NO_DUPLICATE_PROPERTIES: jest.fn().mockReturnValue('')
+      };
+      const constrainFunction = defaultPropertyKeyConstraints(mockedConstraintCallbacks);
+      constrainFunction({constrainedObjectModel, objectModel, propertyKey: ''});
+      //Expect all callbacks to be called
+      for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) {
+        expect(jestMockCallback).toHaveBeenCalled();
+      }
+    });
+    test('should be able to overwrite one hooks', () => {
+      //All but NAMING_FORMATTER, as we customize that
+      const spies = [
+        jest.spyOn(DefaultPropertyKeyConstraints, 'NO_SPECIAL_CHAR'),
+        jest.spyOn(DefaultPropertyKeyConstraints, 'NO_NUMBER_START_CHAR'),
+        jest.spyOn(DefaultPropertyKeyConstraints, 'NO_EMPTY_VALUE'),
+        jest.spyOn(DefaultPropertyKeyConstraints, 'NO_RESERVED_KEYWORDS'),
+        jest.spyOn(DefaultPropertyKeyConstraints, 'NO_DUPLICATE_PROPERTIES')
+      ];
+      const jestCallback = jest.fn().mockReturnValue('');
+      const constrainFunction = defaultPropertyKeyConstraints({NAMING_FORMATTER: jestCallback});
+      const constrainedValue = constrainFunction({constrainedObjectModel, objectModel, propertyKey: ''});
+      expect(constrainedValue).toEqual('');
+      for (const jestMockCallback of spies) {
+        expect(jestMockCallback).toHaveBeenCalled();
+      }
+    });
+  });
+});