Skip to main content
The Intent.Modules.Common.CSharp module provides comprehensive support for generating C# code, including template base classes, fluent code builders, and type resolution utilities.

Template Base Class

All C# templates inherit from CSharpTemplateBase<TModel>, which provides:
  • Automatic using directive management: Automatically adds and organizes using statements
  • Type resolution: Resolves fully qualified type names while avoiding conflicts
  • NuGet dependency management: Register package dependencies for your generated code
  • Namespace normalization: Intelligently shortens type references based on context
  • Roslyn-based code merging: Safely merge generated code with user modifications

Basic Template Structure

public class MyTemplate : CSharpTemplateBase<ClassModel>
{
    public const string TemplateId = "MyCompany.MyTemplate";

    public MyTemplate(IOutputTarget outputTarget, ClassModel model) 
        : base(TemplateId, outputTarget, model)
    {
        // Add type sources for resolving other templates
        AddTypeSource("OtherTemplate.TemplateId");
    }

    protected override CSharpFileConfig DefineFileConfig()
    {
        return new CSharpFileConfig(
            className: Model.Name,
            @namespace: $"{OutputTarget.Name}.Domain",
            relativeLocation: "Domain");
    }

    public override string TransformText()
    {
        return $@"
using System;

namespace {Namespace}
{{
    public class {ClassName}
    {{
        // Generated code
    }}
}}";
    }
}

Key Properties and Methods

// Access the configured namespace and class name
public string Namespace { get; }
public string ClassName { get; }

C# Code Builder (CSharpFile)

The CSharpFile builder provides a fluent API for constructing C# code programmatically:
public class MyBuilderTemplate : CSharpTemplateBase<ClassModel>, ICSharpFileBuilderTemplate
{
    public CSharpFile CSharpFile { get; }

    public MyBuilderTemplate(IOutputTarget outputTarget, ClassModel model)
        : base(TemplateId, outputTarget, model)
    {
        CSharpFile = new CSharpFile(this.GetNamespace(), this.GetFolderPath())
            .AddClass(model.Name, @class => 
            {
                @class
                    .WithBaseType("BaseEntity")
                    .ImplementsInterface("IEntity")
                    .AddConstructor(ctor =>
                    {
                        ctor.AddParameter("string", "id", param => param.IntroduceReadonlyField());
                    })
                    .AddProperty("string", "Name", prop => 
                    {
                        prop.Getter.WithExpressionImplementation("_name");
                        prop.Setter.WithExpressionImplementation("_name = value");
                    })
                    .AddMethod("void", "DoSomething", method =>
                    {
                        method
                            .AddParameter("string", "input")
                            .AddStatement("Console.WriteLine(input);");
                    });
            });
    }

    protected override CSharpFileConfig DefineFileConfig() => CSharpFile.GetConfig();

    public override string TransformText() => CSharpFile.ToString();
}

Building Classes

CSharpFile
    .AddClass("MyClass", @class =>
    {
        @class
            .Public() // or Private(), Protected(), Internal()
            .Partial()
            .Abstract()
            .Sealed()
            .WithBaseType("BaseClass")
            .ImplementsInterface("IMyInterface")
            .AddGenericParameter("T")
            .AddGenericTypeConstraint("T", constraint => 
            {
                constraint.AddType("class");
                constraint.AddType("new()");
            });
    });

Advanced Features

@class.AddAttribute("Serializable");

@class.AddAttribute("DataContract", attr =>
{
    attr.AddArgument("Namespace", "\"http://example.com\"");
});

// On methods
method.AddAttribute("HttpGet", attr => 
{
    attr.AddArgument("\"api/users/{id}\"");
});

Type Resolution

Intent’s C# type resolver intelligently handles:
  • Namespace conflicts: Automatically qualifies types when there are naming conflicts
  • Generic types: Properly formats generic types with their type parameters
  • Nullable reference types: Respects C# 8+ nullable annotations
  • Collection types: Formats collection types according to configured patterns
// Configure type sources
AddTypeSource("Domain.Entity.Template", "List<{0}>");

// Get type info with nullable awareness
if (IsNullableReferenceType(typeReference))
{
    // Handle nullable reference type
}

if (IsNonNullableReferenceType(typeReference))
{
    // Handle non-nullable reference type
}

File Configuration

The CSharpFileConfig class controls how files are generated:
return new CSharpFileConfig(
    className: "MyClass",
    @namespace: "MyCompany.Domain",
    relativeLocation: "Domain/Entities",
    overwriteBehaviour: OverwriteBehaviour.Always,
    fileName: "MyClass",
    fileExtension: "cs")
    .ConfigureRoslynWeaver(config =>
    {
        config.WithTagMode(RoslynTagMode.Explicit);
    });

Best Practices

For anything beyond simple string templates, use CSharpFile and its builder API. It handles indentation, formatting, and syntax correctly.
Always use GetTypeName() and related methods instead of hardcoding type names. This ensures proper namespace management and conflict resolution.
Call AddNugetDependency() and AddTypeSource() in your template constructor to ensure dependencies are available during code generation.
For complex builder scenarios, use CSharpFile.OnBuild() to defer configuration until after all decorators have run:
CSharpFile.OnBuild(file =>
{
    // Configuration that depends on decorators
});

See Also

Java Support

Generate Java code with similar capabilities

TypeScript Support

Generate TypeScript and JavaScript code

Template Basics

Learn about Intent templates

Code Management

Understand how Intent merges generated code

Build docs developers (and LLMs) love