Skip to main content
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>)
defaultNullableFormatter
INullableFormatter
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?>

Collection Formatters

CSharpCollectionFormatter

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);

Common Collection Formats

List<T>

List<{0}>
Mutable list implementation

IEnumerable<T>

IEnumerable<{0}>
Enumerable sequence interface

ICollection<T>

ICollection<{0}>
Collection interface with count

Array

{0}[]
Fixed-size array

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

Type
string
The C# type name (can include namespace: “System.Uri”)
Namespace
string
Optional explicit namespace
Is Primitive
bool
default:"true"
Whether the type is a primitive/value type
Is Collection
bool
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);
    }
}

Collection Format Override

// 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

Build docs developers (and LLMs) love