The Type Resolution API converts Intent Architect metadata type references into language-specific type names (e.g., C#, TypeScript, Java). This is essential for generating correctly-typed code from your metadata models.
CSharpTypeResolver
C# implementation of the type resolver that converts ITypeReference metadata into C# type strings.
Constructor
public CSharpTypeResolver (
CSharpCollectionFormatter defaultCollectionFormatter ,
INullableFormatter defaultNullableFormatter ,
ISoftwareFactoryExecutionContext executionContext )
defaultCollectionFormatter
CSharpCollectionFormatter
Formatter for collection types (e.g., List<T>, IEnumerable<T>)
Formatter for nullable reference types
executionContext
ISoftwareFactoryExecutionContext
Execution context providing access to metadata manager and application config
Usage in Templates
Type resolvers are typically used through the template’s GetTypeName methods:
public class MyTemplate : CSharpTemplateBase < ClassModel >
{
public MyTemplate ( IOutputTarget outputTarget , ClassModel model )
: base ( "Template.Id" , outputTarget , model )
{
}
public string GetPropertyType ( AttributeModel attribute )
{
// Resolve type reference to C# type name
return GetTypeName ( attribute . TypeReference );
}
public override string RunTemplate ()
{
foreach ( var attribute in Model . Attributes )
{
var typeName = GetTypeName ( attribute . TypeReference );
// Use typeName in code generation
}
return TransformText ();
}
}
Type Resolution Features
Primitive Type Mapping
The resolver maps Intent Architect primitive types to C# types:
// Metadata type -> C# type
bool -> bool
int -> int
long -> long
decimal -> decimal
string -> string
guid -> Guid ( System . Guid )
datetime -> DateTime ( System . DateTime )
datetimeoffset -> DateTimeOffset ( System . DateTimeOffset )
date -> DateOnly ( System . DateOnly )
binary -> byte []
Collection Types
Collections are formatted based on the configured CSharpCollectionFormatter:
// Examples with List<T> collection formatter
ITypeReference with IsCollection = true - > List < T >
// Examples with IEnumerable<T> collection formatter
ITypeReference with IsCollection = true - > IEnumerable < T >
// Array types
byte [] -> byte []
string [] -> string []
Nullable Types
Nullability is handled based on the configured INullableFormatter:
// Value types with IsNullable = true
int ? -> int ?
Guid ? -> Guid ?
// Reference types (C# 8.0+ nullable reference types)
string ( IsNullable = true ) -> string ?
MyClass ( IsNullable = true ) -> MyClass ?
Generic Types
Generic type parameters are resolved recursively:
// Single type parameter
Task < string > - > Task < string >
// Multiple type parameters
Dictionary < string , int > - > Dictionary < string , int >
// Nested generics
Task < List < string >> - > Task < List < string >>
// Generic with nullable
Task < MyClass ?> - > Task < MyClass ?>
Defines how collection types are formatted:
// Create collection formatters
var listFormatter = CSharpCollectionFormatter . Create ( "List<{0}>" );
var enumerableFormatter = CSharpCollectionFormatter . Create ( "IEnumerable<{0}>" );
var arrayFormatter = CSharpCollectionFormatter . Create ( "{0}[]" );
// Use in resolver
var resolver = new CSharpTypeResolver (
defaultCollectionFormatter : listFormatter ,
defaultNullableFormatter : nullableFormatter ,
executionContext : ExecutionContext );
List<T> Mutable list implementation
IEnumerable<T> Enumerable sequence interface
ICollection<T> Collection interface with count
Stereotype-Based Type Mapping
The resolver supports custom type mapping via the “C#” stereotype:
// In your designer, apply C# stereotype to a type:
// Stereotype: C#
// Type: "System.Uri"
// Namespace: "System"
// Is Primitive: false
// Is Collection: false
// The resolver will use these stereotype properties:
var typeReference = element . TypeReference ;
var resolvedType = GetTypeName ( typeReference );
// Result: "Uri" with namespace "System"
C# Stereotype Properties
The C# type name (can include namespace: “System.Uri”)
Optional explicit namespace
Whether the type is a primitive/value type
Override collection detection
Advanced Usage
Custom Type Sources
Templates can register type sources for resolving custom types:
public class MyTemplate : CSharpTemplateBase < ClassModel >
{
public MyTemplate ( IOutputTarget outputTarget , ClassModel model )
: base ( "Template.Id" , outputTarget , model )
{
// Register type source for resolving to other templates
AddTypeSource ( "Other.Template.Id" );
AddTypeSource ( "Another.Template.Id" , "List<{0}>" );
}
public string GetDependentType ( ITypeReference typeReference )
{
// Will resolve to types from registered type sources
return GetTypeName ( typeReference );
}
}
// Override collection format for specific type reference
var typeName = GetTypeName (
attribute . TypeReference ,
collectionFormat : "IReadOnlyList<{0}>" );
Type Resolution with Generics
// Resolve generic type with parameters
public string GetGenericType ( ITypeReference typeReference )
{
if ( typeReference . GenericTypeParameters . Any ())
{
var baseType = typeReference . Element . Name ;
var typeArgs = typeReference . GenericTypeParameters
. Select ( x => GetTypeName ( x ))
. ToList ();
return $" { baseType } < { string . Join ( ", " , typeArgs )} >" ;
}
return GetTypeName ( typeReference );
}
Template Integration
The type resolver is integrated into template base classes:
public abstract class CSharpTemplateBase < TModel > : IntentTemplateBase < TModel >
{
protected CSharpTemplateBase (
string templateId ,
IOutputTarget outputTarget ,
TModel model )
: base ( templateId , outputTarget , model )
{
var collectionFormatter = CSharpCollectionFormatter . Create (
GetCollectionFormat ());
var nullableFormatter = new CSharpNullableFormatter (
IsNullableAwareContext ());
// Type resolver is available through GetTypeName methods
}
protected virtual string GetCollectionFormat ()
{
return "List<{0}>" ; // Default collection format
}
protected virtual bool IsNullableAwareContext ()
{
return true ; // Enable nullable reference types
}
}
Type Parsing
The resolver can parse C# type strings back into type references:
// Parse C# type string
if ( CSharpTypeParser . TryParse ( "List<string>" , out var parsedType ))
{
// parsedType is a CSharpType representing the parsed structure
}
// Supported formats:
// - Simple types: "string", "int"
// - Generic types: "List<string>", "Dictionary<string, int>"
// - Nullable types: "int?", "string?"
// - Array types: "byte[]", "string[]"
// - Nested generics: "Task<List<string>>"
Best Practices
Use Template Methods Always use template’s GetTypeName methods rather than creating resolver instances directly
Register Type Sources Register all template dependencies as type sources for cross-template type resolution
Consistent Collection Format Use the same collection format throughout your module for consistency
Handle Null References Check for null type references before resolving, especially for optional attributes
Common Patterns
Property Type Generation
foreach ( var attribute in Model . Attributes )
{
var typeName = GetTypeName ( attribute . TypeReference );
var propertyName = attribute . Name . ToPascalCase ();
WriteLine ( $"public { typeName } { propertyName } {{ get; set; }}" );
}
Method Signature Generation
public string GenerateMethod ( OperationModel operation )
{
var returnType = GetTypeName ( operation . TypeReference );
var parameters = operation . Parameters
. Select ( p => $" { GetTypeName ( p . TypeReference )} { p . Name . ToCamelCase ()} " )
. ToList ();
return $"public { returnType } { operation . Name } ( { string . Join ( ", " , parameters )} )" ;
}
See Also