Skip to content

Nextended.CodeGen

📚 Full API reference — every public type and member, generated from the compiled assembly.

🇩🇪 Diese Seite auf Deutsch

Compile-time source code generation from various sources including classes, JSON, XML, and Excel files.

Overview

Nextended.CodeGen is a Roslyn source generator that automatically generates code at compile-time. It supports DTO generation from attributes, class generation from JSON/XML structures, and model generation from Excel spreadsheets.

Note: This package is in early testing stage. The API may change in future releases.

Installation

bash
dotnet add package Nextended.CodeGen
dotnet add package Nextended.Core

Key Features

1. DTO Generation from Attributes

Automatically generate Data Transfer Objects (DTOs) from your domain classes.

csharp
using Nextended.Core.Attributes;

[AutoGenerateDto(
    ToDtoMethodName = "ToDto",
    ToSourceMethodName = "ToEntity",
    IsComCompatible = true,
    Namespace = "MyApp.Dtos"
)]
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string Password { get; set; } // Can be excluded in DTO
}

Generated DTO:

csharp
namespace MyApp.Dtos
{
    public class UserDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
    }
}

2. Class Generation from JSON/XML

Generate strongly-typed classes from configuration files or API responses.

json
// appsettings.json
{
  "Database": {
    "ConnectionString": "Server=localhost;...",
    "MaxPoolSize": 100
  },
  "Api": {
    "BaseUrl": "https://api.example.com",
    "Timeout": 30
  }
}

Configuration:

json
{
  "StructureGenerations": [
    {
      "SourceFile": "/Sources/appsettings.json",
      "RootClassName": "ServerConfiguration",
      "Namespace": "MyApp.Configuration"
    }
  ]
}

Generated class:

csharp
namespace MyApp.Configuration
{
    public class ServerConfiguration
    {
        public Database Database { get; set; }
        public Api Api { get; set; }
    }
    
    public class Database
    {
        public string ConnectionString { get; set; }
        public int MaxPoolSize { get; set; }
    }
    
    public class Api
    {
        public string BaseUrl { get; set; }
        public int Timeout { get; set; }
    }
}

3. Excel to Class Generation

Generate data classes and static lookup tables from Excel spreadsheets.

json
{
  "ExcelGenerations": [
    {
      "ModelType": "RecordStruct",
      "SourceFile": "/Sources/countries.xlsx",
      "Namespace": "MyApp.Data",
      "RootClassName": "Country",
      "KeyColumn": "A",
      "HeaderRowIndex": 1,
      "DataStartRowIndex": 2,
      "GenerateModelClass": true,
      "GenerateStaticTable": true,
      "StaticClassName": "Countries",
      "GenerateAllCollection": true
    }
  ]
}

Configuration

Project Setup

  1. Add packages to your .csproj:
xml
<ItemGroup>
  <PackageReference Include="Nextended.CodeGen" Version="10.1.22" />
  <PackageReference Include="Nextended.Core" Version="10.1.22" PrivateAssets="all" GeneratePathProperty="true" />
</ItemGroup>
  1. Create CodeGen.config.json:
json
{
  "DtoGeneration": {
    "ComIdClassName": "ComGuids",
    "ComIdClassPropertyFormat": "Id{0}",
    "ComIdClassModifier": "Public",
    "OneFilePerClass": true,
    "CreateRegions": true,
    "CreateComments": true,
    "GeneratePartial": true,
    "Namespace": "MyApp.Generated",
    "Suffix": "Dto"
  },
  "StructureGenerations": [
    {
      "SourceFile": "/Sources/appsettings.json",
      "RootClassName": "AppSettings",
      "Namespace": "MyApp.Configuration"
    }
  ]
}
  1. Add config file to .csproj:
xml
<ItemGroup>
  <AdditionalFiles Include="CodeGen.config.json" />
</ItemGroup>

Usage Examples

Basic DTO Generation

csharp
using Nextended.Core.Attributes;

[AutoGenerateDto]
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public DateTime CreatedDate { get; set; }
}

// Usage after build
var product = new Product 
{ 
    Id = 1, 
    Name = "Widget", 
    Price = 99.99m 
};

var dto = product.ToDto(); // Generated extension method
var backToEntity = dto.ToEntity(); // Round-trip conversion

Advanced DTO Configuration

csharp
[AutoGenerateDto(
    Suffix = "Dto",
    Prefix = null,
    ToDtoMethodName = "ToTransferObject",
    ToSourceMethodName = "ToModel",
    Namespace = "MyApp.Dtos",
    IsComCompatible = true,
    GeneratePartial = true
)]
public class Order
{
    public int Id { get; set; }
    public string OrderNumber { get; set; }
    public List<OrderItem> Items { get; set; }
    public Customer Customer { get; set; }
    
    [IgnoreInDto]
    public string InternalNotes { get; set; }
}

Configuration from JSON

appsettings.json:

json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=MyDb;..."
  },
  "Features": {
    "EnableCache": true,
    "MaxRetries": 3
  }
}

CodeGen.config.json:

json
{
  "StructureGenerations": [
    {
      "SourceFile": "/Sources/appsettings.json",
      "RootClassName": "AppConfiguration",
      "Namespace": "MyApp.Config",
      "Ignore": ["Logging.LogLevel.Microsoft"]
    }
  ]
}

Usage:

csharp
var config = new AppConfiguration();
var connectionString = config.ConnectionStrings.DefaultConnection;

Excel Data Generation

Excel file structure:

CodeNameCountryActive
USUnited StatesUSAYes
UKUnited KingdomGBRYes
CACanadaCANYes

Configuration:

json
{
  "ExcelGenerations": [
    {
      "SourceFile": "/Sources/locations.xlsx",
      "Namespace": "MyApp.Data",
      "RootClassName": "Location",
      "KeyColumn": "A",
      "StaticClassName": "Locations",
      "GenerateStaticTable": true,
      "ColumnMappings": {
        "Code": "LocationCode",
        "Name": "LocationName"
      }
    }
  ]
}

Generated code:

csharp
public record struct Location
{
    public string LocationCode { get; init; }
    public string LocationName { get; init; }
    public string Country { get; init; }
    public string Active { get; init; }
}

public static class Locations
{
    public static Location US => new() 
    { 
        LocationCode = "US", 
        LocationName = "United States",
        Country = "USA",
        Active = "Yes"
    };
    
    public static Location UK => new() 
    { 
        LocationCode = "UK", 
        LocationName = "United Kingdom",
        Country = "GBR",
        Active = "Yes"
    };
    
    public static IReadOnlyList<Location> All { get; } = new[]
    {
        US, UK, CA
    };
}

Usage:

csharp
var usLocation = Locations.US;
var allLocations = Locations.All;

Detailed Generation Examples

JSON Structure Generation

Generate strongly-typed classes from JSON files such as configuration files, API responses, or data schemas.

Example 1: Application Configuration

Source File: appsettings.json

json
{
  "Database": {
    "ConnectionString": "Server=localhost;Database=MyApp;",
    "Timeout": 30,
    "MaxRetryCount": 3
  },
  "Redis": {
    "Host": "localhost",
    "Port": 6379,
    "DefaultDatabase": 0
  },
  "Security": {
    "JwtSecret": "your-secret-key",
    "TokenExpirationMinutes": 60,
    "EnableTwoFactor": true
  },
  "Features": {
    "EnableCache": true,
    "EnableLogging": true,
    "MaxUploadSizeMB": 10
  }
}

Configuration: CodeGen.config.json

json
{
  "StructureGenerations": [
    {
      "SourceFile": "/Sources/appsettings.json",
      "RootClassName": "ApplicationSettings",
      "Namespace": "MyApp.Configuration",
      "Prefix": "App",
      "OutputPath": "./Generated/Configuration/"
    }
  ]
}

Generated Classes:

csharp
namespace MyApp.Configuration
{
    public class AppApplicationSettings
    {
        public Database Database { get; set; }
        public Redis Redis { get; set; }
        public Security Security { get; set; }
        public Features Features { get; set; }
    }

    public class Database
    {
        public string ConnectionString { get; set; }
        public int Timeout { get; set; }
        public int MaxRetryCount { get; set; }
    }

    public class Redis
    {
        public string Host { get; set; }
        public int Port { get; set; }
        public int DefaultDatabase { get; set; }
    }

    public class Security
    {
        public string JwtSecret { get; set; }
        public int TokenExpirationMinutes { get; set; }
        public bool EnableTwoFactor { get; set; }
    }

    public class Features
    {
        public bool EnableCache { get; set; }
        public bool EnableLogging { get; set; }
        public int MaxUploadSizeMB { get; set; }
    }
}

Usage:

csharp
using MyApp.Configuration;

// Use the generated strongly-typed configuration
var settings = new AppApplicationSettings
{
    Database = new Database 
    { 
        ConnectionString = "...",
        Timeout = 30 
    }
};

// Access with IntelliSense support
var timeout = settings.Database.Timeout;
var enableCache = settings.Features.EnableCache;

Example 2: API Response Schema

Source File: user-api-response.json

json
{
  "user": {
    "id": 123,
    "username": "john_doe",
    "email": "john@example.com",
    "profile": {
      "firstName": "John",
      "lastName": "Doe",
      "avatar": "https://example.com/avatar.jpg",
      "bio": "Software Developer"
    },
    "settings": {
      "notifications": {
        "email": true,
        "push": false,
        "sms": true
      },
      "privacy": {
        "profileVisible": true,
        "showEmail": false
      }
    },
    "metadata": {
      "createdAt": "2024-01-01T00:00:00Z",
      "lastLogin": "2024-06-15T10:30:00Z",
      "accountStatus": "active"
    }
  }
}

Configuration:

json
{
  "StructureGenerations": [
    {
      "SourceFile": "/Sources/user-api-response.json",
      "RootClassName": "UserApiResponse",
      "Namespace": "MyApp.ApiModels",
      "Ignore": ["metadata.lastLogin"]
    }
  ]
}

Generated Classes:

csharp
namespace MyApp.ApiModels
{
    public class UserApiResponse
    {
        public User User { get; set; }
    }

    public class User
    {
        public int Id { get; set; }
        public string Username { get; set; }
        public string Email { get; set; }
        public Profile Profile { get; set; }
        public Settings Settings { get; set; }
        public Metadata Metadata { get; set; }
    }

    public class Profile
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Avatar { get; set; }
        public string Bio { get; set; }
    }

    public class Settings
    {
        public Notifications Notifications { get; set; }
        public Privacy Privacy { get; set; }
    }

    public class Notifications
    {
        public bool Email { get; set; }
        public bool Push { get; set; }
        public bool Sms { get; set; }
    }

    public class Privacy
    {
        public bool ProfileVisible { get; set; }
        public bool ShowEmail { get; set; }
    }

    public class Metadata
    {
        public string CreatedAt { get; set; }
        public string AccountStatus { get; set; }
    }
}

XML Structure Generation

Generate classes from XML files using the same configuration structure.

Source File: config.xml

xml
<?xml version="1.0" encoding="utf-8"?>
<Configuration>
  <Server>
    <Host>localhost</Host>
    <Port>8080</Port>
    <EnableSSL>true</EnableSSL>
  </Server>
  <Logging>
    <Level>Information</Level>
    <FilePath>./logs/app.log</FilePath>
    <MaxFileSizeMB>50</MaxFileSizeMB>
  </Logging>
</Configuration>

Configuration:

json
{
  "StructureGenerations": [
    {
      "SourceFile": "/Sources/config.xml",
      "RootClassName": "ServerConfiguration",
      "Namespace": "MyApp.XmlConfig"
    }
  ]
}

Generated Classes:

csharp
namespace MyApp.XmlConfig
{
    public class ServerConfiguration
    {
        public Server Server { get; set; }
        public Logging Logging { get; set; }
    }

    public class Server
    {
        public string Host { get; set; }
        public int Port { get; set; }
        public bool EnableSSL { get; set; }
    }

    public class Logging
    {
        public string Level { get; set; }
        public string FilePath { get; set; }
        public int MaxFileSizeMB { get; set; }
    }
}

Excel Generation (Advanced)

Generate data models and static lookup tables from Excel spreadsheets. This is particularly useful for reference data, lookup tables, and configuration data.

Example 1: Country/Region Codes

Excel File Structure: countries.xlsx

CodeNameISO3NumericCodePhonePrefixCapital
USUnited StatesUSA840+1Washington, D.C.
GBUnited KingdomGBR826+44London
DEGermanyDEU276+49Berlin
JPJapanJPN392+81Tokyo
CACanadaCAN124+1Ottawa

Configuration:

json
{
  "ExcelGenerations": [
    {
      "ModelType": "RecordStruct",
      "SourceFile": "/Sources/countries.xlsx",
      "SheetName": null,
      "Namespace": "MyApp.Data.Geography",
      "RootClassName": "Country",
      "KeyColumn": "A",
      "HeaderRowIndex": 1,
      "DataStartRowIndex": 2,
      "GenerateModelClass": true,
      "GenerateStaticTable": true,
      "StaticClassName": "Countries",
      "GenerateAllCollection": true,
      "ColumnMappings": {
        "Code": "IsoCode",
        "ISO3": "Iso3Code"
      },
      "PropertyTypeOverrides": {
        "NumericCode": "int",
        "PhonePrefix": "string"
      }
    }
  ]
}

Generated Code:

csharp
namespace MyApp.Data.Geography
{
    // Model class
    public record struct Country
    {
        public string IsoCode { get; init; }
        public string Name { get; init; }
        public string Iso3Code { get; init; }
        public int NumericCode { get; init; }
        public string PhonePrefix { get; init; }
        public string Capital { get; init; }
    }

    // Static lookup table
    public static class Countries
    {
        public static Country US => new Country
        {
            IsoCode = "US",
            Name = "United States",
            Iso3Code = "USA",
            NumericCode = 840,
            PhonePrefix = "+1",
            Capital = "Washington, D.C."
        };

        public static Country GB => new Country
        {
            IsoCode = "GB",
            Name = "United Kingdom",
            Iso3Code = "GBR",
            NumericCode = 826,
            PhonePrefix = "+44",
            Capital = "London"
        };

        public static Country DE => new Country
        {
            IsoCode = "DE",
            Name = "Germany",
            Iso3Code = "DEU",
            NumericCode = 276,
            PhonePrefix = "+49",
            Capital = "Berlin"
        };

        // ... more countries

        public static IReadOnlyList<Country> All { get; } = new[]
        {
            US, GB, DE, JP, CA
        };

        public static Country GetByCode(string code)
        {
            return All.FirstOrDefault(c => c.IsoCode == code);
        }
    }
}

Usage:

csharp
using MyApp.Data.Geography;

// Access individual countries
var usa = Countries.US;
Console.WriteLine($"{usa.Name} - {usa.PhonePrefix}");

// Get all countries
var allCountries = Countries.All;
foreach (var country in allCountries)
{
    Console.WriteLine($"{country.Name} ({country.IsoCode})");
}

// Lookup by code
var germany = Countries.GetByCode("DE");

Example 2: Product Categories with Hierarchies

Excel File: product-categories.xlsx

CategoryIdCategoryNameParentIdSortOrderActiveDescription
1Electronics1YElectronic devices and accessories
2Computers11YDesktop and laptop computers
3Laptops21YPortable computers
4Desktops22YDesktop computers
5Smartphones12YMobile phones

Configuration:

json
{
  "ExcelGenerations": [
    {
      "ModelType": "Class",
      "SourceFile": "/Sources/product-categories.xlsx",
      "Namespace": "MyApp.Catalog",
      "RootClassName": "ProductCategory",
      "KeyColumn": "A",
      "HeaderRowIndex": 1,
      "DataStartRowIndex": 2,
      "GenerateModelClass": true,
      "GenerateStaticTable": true,
      "StaticClassName": "ProductCategories",
      "GenerateAllCollection": true,
      "PropertyTypeOverrides": {
        "CategoryId": "int",
        "ParentId": "int?",
        "SortOrder": "int",
        "Active": "bool"
      },
      "ColumnMappings": {
        "Active": "IsActive"
      }
    }
  ]
}

Generated Classes:

csharp
namespace MyApp.Catalog
{
    public class ProductCategory
    {
        public int CategoryId { get; set; }
        public string CategoryName { get; set; }
        public int? ParentId { get; set; }
        public int SortOrder { get; set; }
        public bool IsActive { get; set; }
        public string Description { get; set; }
    }

    public static class ProductCategories
    {
        public static ProductCategory Electronics => new ProductCategory
        {
            CategoryId = 1,
            CategoryName = "Electronics",
            ParentId = null,
            SortOrder = 1,
            IsActive = true,
            Description = "Electronic devices and accessories"
        };

        public static ProductCategory Computers => new ProductCategory
        {
            CategoryId = 2,
            CategoryName = "Computers",
            ParentId = 1,
            SortOrder = 1,
            IsActive = true,
            Description = "Desktop and laptop computers"
        };

        // ... more categories

        public static IReadOnlyList<ProductCategory> All { get; } = new[]
        {
            Electronics,
            Computers,
            Laptops,
            Desktops,
            Smartphones
        };

        public static ProductCategory GetById(int id)
        {
            return All.FirstOrDefault(c => c.CategoryId == id);
        }

        public static IEnumerable<ProductCategory> GetChildren(int parentId)
        {
            return All.Where(c => c.ParentId == parentId);
        }
    }
}

Usage:

csharp
using MyApp.Catalog;

// Get root categories
var rootCategories = ProductCategories.All
    .Where(c => c.ParentId == null);

// Get children of Electronics
var electronicsChildren = ProductCategories.GetChildren(1);

// Build category tree
var category = ProductCategories.Computers;
Console.WriteLine($"{category.CategoryName}: {category.Description}");

Excel Generation Configuration Options

PropertyTypeDescription
ModelTypestringType of model to generate: Class, Struct, Record, RecordStruct
SourceFilestringPath to the Excel file (relative to project root)
SheetNamestring?Specific sheet name. If null, uses first sheet
NamespacestringNamespace for generated classes
RootClassNamestringName of the model class
KeyColumnstringColumn letter to use as key (e.g., "A", "B")
HeaderRowIndexintRow number containing column headers (1-based)
DataStartRowIndexintFirst row containing data (1-based)
GenerateModelClassboolWhether to generate the model class
GenerateStaticTableboolWhether to generate static lookup class
StaticClassNamestringName of the static lookup class
GenerateAllCollectionboolWhether to generate All property with all records
ColumnMappingsobjectDictionary mapping Excel column names to property names
PropertyTypeOverridesobjectDictionary specifying custom types for properties
OutputPathstring?Custom output path for generated files

Tips for Excel Generation

  1. Column Headers: Use clear, descriptive names in the header row. These become property names.

  2. Data Types: By default, all columns are strings. Use PropertyTypeOverrides to specify correct types:

    json
    "PropertyTypeOverrides": {
      "Id": "int",
      "Price": "decimal",
      "IsActive": "bool",
      "CreatedDate": "DateTime"
    }
  3. Naming: Use ColumnMappings to rename columns that don't follow C# naming conventions:

    json
    "ColumnMappings": {
      "Full Name": "FullName",
      "E-Mail": "Email",
      "ZIP Code": "PostalCode"
    }
  4. Model Types:

    • RecordStruct: Immutable, value type, best for small lookup data
    • Record: Immutable, reference type, good for DTOs
    • Class: Mutable, reference type, most flexible
    • Struct: Mutable, value type, for small data structures
  5. Key Column: Choose a unique identifier column (usually first column) for generating property accessors.

AutoGenerateDto Attribute Reference

The AutoGenerateDto attribute is applied to classes or enums to generate DTOs at compile-time.

Basic Usage

csharp
[AutoGenerateDto]
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}
// Generates: UserDto class with mapping methods

Attribute Properties

Namespace and Naming

PropertyTypeDefaultDescription
NamespacestringnullTarget namespace for generated DTO. If null, uses source class namespace
Prefixstring""Prefix for generated class name (e.g., "Com" → "ComUserDto")
Suffixstring"Dto"Suffix for generated class name (e.g., "Dto" → "UserDto")
GeneratedClassNamestringnullOverride complete class name. Ignores prefix/suffix if set

Example:

csharp
[AutoGenerateDto(
    Namespace = "MyApp.Api.Dtos",
    Prefix = "Api",
    Suffix = "Response"
)]
public class User { }
// Generates: MyApp.Api.Dtos.ApiUserResponse

Mapping Configuration

PropertyTypeDefaultDescription
GenerateMappingbooltrueGenerate extension methods for mapping (ToDto/ToSource)
ToDtoMethodNamestringnullName of the method to convert source to DTO (e.g., "ToDto")
ToSourceMethodNamestringnullName of the method to convert DTO back to source (e.g., "ToEntity")

Example:

csharp
[AutoGenerateDto(
    ToDtoMethodName = "ToApiModel",
    ToSourceMethodName = "ToEntity"
)]
public class User { }

// Usage:
var dto = user.ToApiModel();
var entity = dto.ToEntity();

Property Control

PropertyTypeDefaultDescription
PropertiesToIgnorestring[]nullProperty names to exclude from DTO generation

Example:

csharp
[AutoGenerateDto(PropertiesToIgnore = new[] { "Password", "InternalId" })]
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Password { get; set; }  // Not included in DTO
    public Guid InternalId { get; set; }  // Not included in DTO
}

Alternative: Use [IgnoreOnGeneration] attribute on properties:

csharp
public class User
{
    public int Id { get; set; }
    
    [IgnoreOnGeneration]
    public string Password { get; set; }  // Not included in DTO
}

Inheritance and Interfaces

PropertyTypeDefaultDescription
BaseTypestringnullBase type for generated DTO (e.g., "BaseDto")
Interfacesstring[]nullInterfaces to implement (e.g., "IDto", "IValidatable")
AutoGenerateDerivedboolfalseAutomatically generate DTOs for derived classes

Example:

csharp
[AutoGenerateDto(
    BaseType = "EntityBase",
    Interfaces = new[] { "IDto", "IAuditable" }
)]
public class User { }

// Generates:
public class UserDto : EntityBase, IDto, IAuditable
{
    // Properties...
}

COM Compatibility

PropertyTypeDefaultDescription
IsComCompatibleboolfalseGenerate COM-visible and COM-compatible code with GUIDs

Example:

csharp
[AutoGenerateDto(IsComCompatible = true)]
public class User { }

// Generates:
[ComVisible(true)]
[Guid("...")]
public class UserDto
{
    // Properties...
}

Modifiers and Access

PropertyTypeDefaultDescription
ClassModifierModifierPublicAccess modifier for generated class (Public, Internal, Private)
InterfaceModifierModifierPublicAccess modifier for generated interface
DefaultPropertyInterfaceAccessInterfacePropertyGetSetDefault property access in interface (Get, Set, GetSet)

Example:

csharp
[AutoGenerateDto(
    ClassModifier = Modifier.Internal,
    InterfaceModifier = Modifier.Public
)]
public class User { }

Attributes and Metadata

PropertyTypeDefaultDescription
KeepAttributesOnGeneratedClassboolfalseCopy attributes from source class to DTO
KeepAttributesOnGeneratedInterfaceboolfalseCopy attributes from source class to generated interface
KeepPropertyAttributesOnGeneratedClassboolfalseCopy property attributes to DTO properties
KeepPropertyAttributesOnGeneratedInterfaceboolfalseCopy property attributes to interface properties
PreClassStringstringnullString to add before class (e.g., attributes)
PreInterfaceStringstringnullString to add before interface (e.g., attributes)

Example:

csharp
[AutoGenerateDto(
    PreClassString = "[Serializable]\n[JsonObject]",
    KeepPropertyAttributesOnGeneratedClass = true
)]
public class User
{
    [Required]
    [StringLength(100)]
    public string Name { get; set; }
}

// Generates:
[Serializable]
[JsonObject]
public class UserDto
{
    [Required]
    [StringLength(100)]
    public string Name { get; set; }
}

Namespaces and Using Directives

PropertyTypeDefaultDescription
Usingsstring[]nullAdditional using directives
AddReferencedNamespacesUsingsboolfalseAdd usings for all referenced namespaces
AddContainingNamespaceUsingsboolfalseAdd using for source class namespace

Example:

csharp
[AutoGenerateDto(
    Usings = new[] { "System.ComponentModel.DataAnnotations", "MyApp.Helpers" },
    AddReferencedNamespacesUsings = true
)]
public class User { }

Complete Example

csharp
using Nextended.Core.Attributes;
using Nextended.Core.Enums;

[AutoGenerateDto(
    // Naming
    Namespace = "MyApp.Api.Dtos",
    Suffix = "Response",
    
    // Mapping
    ToDtoMethodName = "ToApiResponse",
    ToSourceMethodName = "ToEntity",
    
    // Inheritance
    BaseType = "BaseResponse",
    Interfaces = new[] { "IApiResponse" },
    
    // Properties
    PropertiesToIgnore = new[] { "PasswordHash", "InternalData" },
    
    // Modifiers
    ClassModifier = Modifier.Public,
    
    // Attributes
    KeepPropertyAttributesOnGeneratedClass = true,
    PreClassString = "[Serializable]",
    
    // Namespaces
    Usings = new[] { "System.ComponentModel.DataAnnotations" }
)]
public class User
{
    public int Id { get; set; }
    
    [Required]
    [StringLength(100)]
    public string Username { get; set; }
    
    [EmailAddress]
    public string Email { get; set; }
    
    [IgnoreOnGeneration]
    public string PasswordHash { get; set; }
    
    public DateTime CreatedAt { get; set; }
}

// Usage:
var user = GetUser();
var response = user.ToApiResponse();
var userAgain = response.ToEntity();

Configuration Options

DtoGeneration Options (CodeGen.config.json)

These settings apply to all DTOs unless overridden by attribute properties.

OptionTypeDescription
NamespacestringDefault namespace for generated DTOs
SuffixstringDefault suffix for DTO class names (default: "Dto")
PrefixstringDefault prefix for DTO class names
ToDtoMethodNamestringDefault name of ToDto extension method
ToSourceMethodNamestringDefault name of ToSource extension method
IsComCompatibleboolGenerate COM-compatible GUIDs by default
GeneratePartialboolGenerate as partial classes (default: true)
OneFilePerClassboolCreate separate file per DTO (default: true)
CreateRegionsboolAdd region blocks (default: true)
CreateCommentsboolAdd XML comments (default: true)
ComIdClassNamestringName of the class for COM GUIDs
ComIdClassPropertyFormatstringFormat for COM ID properties
ComIdClassModifierstringModifier for COM ID class
BaseTypestringDefault base type for DTOs
Interfacesstring[]Default interfaces for DTOs
Usingsstring[]Default using directives
KeepAttributesOnGeneratedClassboolCopy attributes from source classes
KeepAttributesOnGeneratedInterfaceboolCopy attributes to interfaces
AddReferencedNamespacesUsingsboolAdd usings for referenced namespaces
AddContainingNamespaceUsingsboolAdd using for source namespaces
PreInterfaceStringstringString before generated interfaces
PreClassStringstringString before generated classes
DefaultMappingSettingsobjectDefault mapping settings configuration
OutputPathstringOutput directory for generated files

StructureGeneration Options (JSON/XML)

Complete reference for generating classes from JSON and XML files.

OptionTypeRequiredDescription
SourceFilestringYesPath to JSON or XML source file (relative to project root)
RootClassNamestringYesName of the root generated class
NamespacestringYesTarget namespace for generated classes
PrefixstringNoPrefix for all generated class names
SuffixstringNoSuffix for all generated class names
Ignorestring[]NoProperty paths to ignore (e.g., "Database.Password")
OutputPathstringNoOutput directory for generated files (if null, added to compilation)

JSON Generation Examples

Basic Configuration File:

appsettings.json:

json
{
  "Database": {
    "ConnectionString": "Server=localhost;Database=MyDb;",
    "MaxPoolSize": 100,
    "EnableLogging": true
  },
  "Api": {
    "BaseUrl": "https://api.example.com",
    "Timeout": 30,
    "ApiKey": "secret-key"
  }
}

Configuration:

json
{
  "StructureGenerations": [
    {
      "SourceFile": "/Config/appsettings.json",
      "RootClassName": "AppSettings",
      "Namespace": "MyApp.Configuration"
    }
  ]
}

Generated Code:

csharp
namespace MyApp.Configuration
{
    public class AppSettings
    {
        public Database Database { get; set; }
        public Api Api { get; set; }
    }

    public class Database
    {
        public string ConnectionString { get; set; }
        public int MaxPoolSize { get; set; }
        public bool EnableLogging { get; set; }
    }

    public class Api
    {
        public string BaseUrl { get; set; }
        public int Timeout { get; set; }
        public string ApiKey { get; set; }
    }
}

Usage:

csharp
var config = new AppSettings
{
    Database = new Database
    {
        ConnectionString = "...",
        MaxPoolSize = 100
    }
};

Advanced with Ignore:

json
{
  "StructureGenerations": [
    {
      "SourceFile": "/Config/appsettings.json",
      "RootClassName": "AppSettings",
      "Namespace": "MyApp.Configuration",
      "Prefix": "Cfg",
      "Ignore": ["Api.ApiKey", "Database.ConnectionString"],
      "OutputPath": "./Generated/Config/"
    }
  ]
}

Complex JSON Structure:

users.json:

json
{
  "users": [
    {
      "id": 1,
      "name": "John Doe",
      "email": "john@example.com",
      "roles": ["admin", "user"],
      "metadata": {
        "lastLogin": "2024-01-15T10:30:00Z",
        "loginCount": 42
      }
    }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 10,
    "total": 100
  }
}

Generated Classes:

csharp
public class UsersRoot
{
    public List<User> Users { get; set; }
    public Pagination Pagination { get; set; }
}

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public List<string> Roles { get; set; }
    public Metadata Metadata { get; set; }
}

public class Metadata
{
    public DateTime LastLogin { get; set; }
    public int LoginCount { get; set; }
}

public class Pagination
{
    public int Page { get; set; }
    public int PageSize { get; set; }
    public int Total { get; set; }
}

XML Generation Examples

Simple XML Configuration:

xml
<!-- config.xml -->
<Configuration>
  <Server>
    <Host>localhost</Host>
    <Port>8080</Port>
    <UseSSL>true</UseSSL>
  </Server>
  <Logging>
    <Level>Information</Level>
    <FilePath>logs/app.log</FilePath>
  </Logging>
</Configuration>

Configuration:

json
{
  "StructureGenerations": [
    {
      "SourceFile": "/Config/config.xml",
      "RootClassName": "Configuration",
      "Namespace": "MyApp.Config"
    }
  ]
}

Generated Code:

csharp
namespace MyApp.Config
{
    public class Configuration
    {
        public Server Server { get; set; }
        public Logging Logging { get; set; }
    }

    public class Server
    {
        public string Host { get; set; }
        public int Port { get; set; }
        public bool UseSSL { get; set; }
    }

    public class Logging
    {
        public string Level { get; set; }
        public string FilePath { get; set; }
    }
}

ExcelGeneration Options

Complete reference for generating classes from Excel spreadsheets.

OptionTypeRequiredDescription
SourceFilestringYesPath to Excel file (.xlsx, .xls)
SheetNamestringNoSpecific sheet name (null for first sheet)
NamespacestringYesTarget namespace for generated classes
RootClassNamestringYesName of the model/record class
StaticClassNamestringNoName of the static lookup class
KeyColumnstringNoColumn letter to use as key (e.g., "A", "B")
HeaderRowIndexintYesRow number containing column headers (1-based)
DataStartRowIndexintYesFirst row number containing data (1-based)
ModelTypestringNoType of model: "Class", "RecordClass", "RecordStruct" (default: "Class")
GenerateModelClassboolNoGenerate the model/record class (default: true)
GenerateStaticTableboolNoGenerate static lookup class with data (default: false)
GenerateAllCollectionboolNoGenerate collection of all records (default: false)
ColumnMappingsDictionary<string,string>NoMap Excel column names to property names
PropertyTypeOverridesDictionary<string,string>NoOverride property types
OutputPathstringNoOutput directory for generated files

Excel Generation Examples

Example 1: Simple Data Table

Excel File (countries.xlsx):

| Code | Name           | Population | Capital    |
|------|----------------|------------|------------|
| US   | United States  | 331000000  | Washington |
| UK   | United Kingdom | 67000000   | London     |
| FR   | France         | 65000000   | Paris      |

Configuration:

json
{
  "ExcelGenerations": [
    {
      "SourceFile": "/Data/countries.xlsx",
      "Namespace": "MyApp.Data",
      "RootClassName": "Country",
      "HeaderRowIndex": 1,
      "DataStartRowIndex": 2,
      "GenerateModelClass": true
    }
  ]
}

Generated Code:

csharp
namespace MyApp.Data
{
    public class Country
    {
        public string Code { get; set; }
        public string Name { get; set; }
        public int Population { get; set; }
        public string Capital { get; set; }
    }
}

Example 2: Static Lookup Table

Configuration:

json
{
  "ExcelGenerations": [
    {
      "SourceFile": "/Data/countries.xlsx",
      "Namespace": "MyApp.Data",
      "RootClassName": "Country",
      "StaticClassName": "Countries",
      "KeyColumn": "A",
      "HeaderRowIndex": 1,
      "DataStartRowIndex": 2,
      "GenerateModelClass": true,
      "GenerateStaticTable": true,
      "GenerateAllCollection": true
    }
  ]
}

Generated Code:

csharp
namespace MyApp.Data
{
    public class Country
    {
        public string Code { get; set; }
        public string Name { get; set; }
        public int Population { get; set; }
        public string Capital { get; set; }
    }

    public static class Countries
    {
        public static Country US { get; } = new Country
        {
            Code = "US",
            Name = "United States",
            Population = 331000000,
            Capital = "Washington"
        };

        public static Country UK { get; } = new Country
        {
            Code = "UK",
            Name = "United Kingdom",
            Population = 67000000,
            Capital = "London"
        };

        public static Country FR { get; } = new Country
        {
            Code = "FR",
            Name = "France",
            Population = 65000000,
            Capital = "Paris"
        };

        public static IReadOnlyList<Country> All { get; } = new[]
        {
            US, UK, FR
        };
    }
}

Usage:

csharp
// Access by key
var us = Countries.US;
Console.WriteLine($"{us.Name}: {us.Population}");

// Access all
foreach (var country in Countries.All)
{
    Console.WriteLine(country.Name);
}

// Find by code
var france = Countries.All.FirstOrDefault(c => c.Code == "FR");

Example 3: Record Struct with Column Mapping

Excel File (products.xlsx):

| Product ID | Product Name    | Unit Price | In Stock |
|------------|-----------------|------------|----------|
| P001       | Widget A        | 19.99      | Yes      |
| P002       | Gadget B        | 29.99      | No       |

Configuration:

json
{
  "ExcelGenerations": [
    {
      "SourceFile": "/Data/products.xlsx",
      "Namespace": "MyApp.Catalog",
      "RootClassName": "Product",
      "ModelType": "RecordStruct",
      "HeaderRowIndex": 1,
      "DataStartRowIndex": 2,
      "ColumnMappings": {
        "Product ID": "ProductId",
        "Product Name": "Name",
        "Unit Price": "Price",
        "In Stock": "Available"
      }
    }
  ]
}

Generated Code:

csharp
namespace MyApp.Catalog
{
    public record struct Product
    {
        public string ProductId { get; init; }
        public string Name { get; init; }
        public double Price { get; init; }
        public string Available { get; init; }
    }
}

Example 4: Custom Property Types

Excel File (locations.xlsx):

| Code | Name        | Coordinates      | Status  | Type      |
|------|-------------|------------------|---------|-----------|
| NYC  | New York    | 40.7128,-74.0060 | Active  | Primary   |
| LAX  | Los Angeles | 34.0522,-118.2437| Active  | Secondary |

Custom Types:

csharp
namespace MyApp.Types
{
    public record struct Coordinates
    {
        public double Latitude { get; init; }
        public double Longitude { get; init; }

        public static Coordinates Parse(string value)
        {
            var parts = value.Split(',');
            return new Coordinates
            {
                Latitude = double.Parse(parts[0]),
                Longitude = double.Parse(parts[1])
            };
        }
    }

    public enum LocationStatus { Active, Inactive }
    public enum LocationType { Primary, Secondary, Tertiary }
}

Configuration:

json
{
  "ExcelGenerations": [
    {
      "SourceFile": "/Data/locations.xlsx",
      "Namespace": "MyApp.Data",
      "RootClassName": "Location",
      "StaticClassName": "Locations",
      "KeyColumn": "A",
      "HeaderRowIndex": 1,
      "DataStartRowIndex": 2,
      "ModelType": "RecordStruct",
      "GenerateStaticTable": true,
      "PropertyTypeOverrides": {
        "Coordinates": "MyApp.Types.Coordinates",
        "Status": "MyApp.Types.LocationStatus",
        "Type": "MyApp.Types.LocationType"
      },
      "ColumnMappings": {
        "Status": "Status",
        "Type": "Type"
      }
    }
  ]
}

Generated Code:

csharp
namespace MyApp.Data
{
    public record struct Location
    {
        public string Code { get; init; }
        public string Name { get; init; }
        public MyApp.Types.Coordinates Coordinates { get; init; }
        public MyApp.Types.LocationStatus Status { get; init; }
        public MyApp.Types.LocationType Type { get; init; }
    }

    public static class Locations
    {
        public static Location NYC { get; } = new Location
        {
            Code = "NYC",
            Name = "New York",
            Coordinates = MyApp.Types.Coordinates.Parse("40.7128,-74.0060"),
            Status = MyApp.Types.LocationStatus.Active,
            Type = MyApp.Types.LocationType.Primary
        };

        public static Location LAX { get; } = new Location
        {
            Code = "LAX",
            Name = "Los Angeles",
            Coordinates = MyApp.Types.Coordinates.Parse("34.0522,-118.2437"),
            Status = MyApp.Types.LocationStatus.Active,
            Type = MyApp.Types.LocationType.Secondary
        };
    }
}

Example 5: Multiple Sheets

json
{
  "ExcelGenerations": [
    {
      "SourceFile": "/Data/masterdata.xlsx",
      "SheetName": "Customers",
      "Namespace": "MyApp.Data",
      "RootClassName": "Customer",
      "HeaderRowIndex": 1,
      "DataStartRowIndex": 2
    },
    {
      "SourceFile": "/Data/masterdata.xlsx",
      "SheetName": "Products",
      "Namespace": "MyApp.Data",
      "RootClassName": "Product",
      "HeaderRowIndex": 1,
      "DataStartRowIndex": 2
    }
  ]
}

Example 6: Complete Real-World Scenario

Excel File (scac_codes.xlsx) - Shipping Carrier Codes:

| SCAC | Carrier Name          | Country | Contact Email      | Active |
|------|-----------------------|---------|--------------------|--------|
| ABCD | ABC Logistics         | USA     | info@abclog.com    | Yes    |
| EFGH | EFG Shipping Co       | USA     | contact@efg.com    | Yes    |
| WXYZ | WXY International     | CAN     | support@wxy.com    | No     |

Configuration:

json
{
  "ExcelGenerations": [
    {
      "SourceFile": "/Sources/scac_codes.xlsx",
      "SheetName": null,
      "Namespace": "Shipping.Data",
      "RootClassName": "ScacCode",
      "StaticClassName": "ScacCodes",
      "KeyColumn": "A",
      "HeaderRowIndex": 1,
      "DataStartRowIndex": 2,
      "ModelType": "RecordStruct",
      "GenerateModelClass": true,
      "GenerateStaticTable": true,
      "GenerateAllCollection": true,
      "ColumnMappings": {
        "SCAC": "Code",
        "Carrier Name": "CarrierName",
        "Contact Email": "Email"
      },
      "PropertyTypeOverrides": {
        "Active": "bool"
      },
      "OutputPath": "./Generated/Shipping/"
    }
  ]
}

Generated Usage:

csharp
using Shipping.Data;

// Lookup by key
var carrier = ScacCodes.ABCD;
Console.WriteLine($"Carrier: {carrier.CarrierName}");

// Search all
var activeCarriers = ScacCodes.All
    .Where(c => c.Active)
    .ToList();

// Use in dropdown
var dropdownItems = ScacCodes.All
    .Select(c => new { Value = c.Code, Label = c.CarrierName })
    .ToList();

Best Practices for Excel Generation

  1. Clean Headers: Use clear, descriptive column headers without special characters
  2. Consistent Data Types: Ensure columns contain consistent data types
  3. Key Columns: Use columns with unique values for KeyColumn
  4. Type Overrides: Use PropertyTypeOverrides for enums, custom types, or specific numeric types
  5. Column Mapping: Use ColumnMappings to create clean property names from complex headers
  6. Model Types:
    • Use RecordStruct for small, immutable data
    • Use RecordClass for immutable reference types
    • Use Class for mutable data that needs modification
  7. Output Path: Use OutputPath to organize generated files separately from source code

The four generators

Nextended.CodeGen is a single Roslyn source generator driven by one CodeGen.config.json (MainConfig). It hosts four independent sub-generators, each configured by its own section:

SectionTypeInputProduces
DtoGenerationDtoGenerationConfigYour classes and enums, annotated with [AutoGenerateDto] / [AutoGenerateCom]DTO classes, interfaces, mapping extension methods, COM id class
StructureGenerations[]ClassStructureCodeGenerationConfigA .json or .xml fileStrongly typed classes / records for that structure
ExcelGenerations[]ExcelGenerationConfigAn .xlsx fileA row model plus a static lookup table
CodeToDocs[]CodeToDocsConfigA folder of source files.txt, .md and/or .html documentation

All four sections are optional. An empty {} config file is valid — the DTO generator then runs purely off the attributes.


Documentation generation (CodeToDocs)

The fourth generator does not emit C#. It reads source files and writes documentation, which is useful for embedding real, always-current code in a docs site, a Blazor sample browser, or an LLM context bundle.

Configuration

json
{
  "CodeToDocs": [
    {
      "DisableGeneration": false,
      "InputFolder": "./Entities",
      "OutputPath": "./Generated/HtmlDocs/",
      "FileIncludePattern": "*.cs",
      "SourceExportTypes": [ "PlainText", "Markdown", "Html" ]
    }
  ]
}
PropertyTypeDefaultPurpose
DisableGenerationboolfalseTurn this entry off without deleting it
InputFolderstringFolder to scan, relative to the project
OutputPathstring?nullWhere to write. When null, output is added to the compilation instead of the disk
FileIncludePatternstringGlob applied inside InputFolder, e.g. *.cs, *.razor, *.html
SourceExportTypesSourceExportType[]Any combination of PlainText (.txt), Markdown (.md), Html (.html)

SourceExportType carries the file extension as a [Description], so the output file name is <source name><extension>.

Output

Markdown wraps each file in a fenced block with the right language hint:

markdown
```csharp
public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
}
```

Html runs the source through ColorCode, so the result is syntax-highlighted markup you can drop straight into a page. PlainText is a verbatim copy — useful when something else does the formatting.

Multiple entries are allowed, so one project can export its entities as Markdown and its Razor components as HTML in the same build:

json
"CodeToDocs": [
  { "InputFolder": "./Entities",   "FileIncludePattern": "*.cs",    "SourceExportTypes": [ "Markdown" ],     "OutputPath": "./Generated/Docs/Entities/" },
  { "InputFolder": "./Components", "FileIncludePattern": "*.razor", "SourceExportTypes": [ "Html" ],         "OutputPath": "./Generated/Docs/Components/" }
]

COM-visible generation

AutoGenerateComAttribute derives from AutoGenerateDtoAttribute with COM-friendly defaults pre-applied:

csharp
public class AutoGenerateComAttribute : AutoGenerateDtoAttribute
{
    public AutoGenerateComAttribute()
    {
        IsComCompatible = true;
        Prefix = "Com";
        Suffix = "";
    }
}

So these two are equivalent:

csharp
[AutoGenerateCom]
public class Address { … }

[AutoGenerateDto(IsComCompatible = true, Prefix = "Com", Suffix = "")]
public class Address { … }

With IsComCompatible = true the generator emits [ComVisible] types with explicit interfaces and stable GUIDs. The GUIDs live in a generated id class so they stay constant across builds — changing them would break registered COM clients.

DTO config propertyDefaultPurpose
ComIdClassName"ComGuids"Name of the generated class holding the GUID constants
ComIdClassPropertyFormat"Id{0}"Format for each constant's name; {0} is the type name
ComIdClassModifierInternalAccessibility of that class (Public, Internal, …)

The sample project has the generated result checked in at Generated/Dtos/ComGuids.g.cs.


Abstract and derived types

Domain models are rarely flat. Four DtoGeneration options control what happens at the abstract boundary:

PropertyDefaultEffect
MakeDtoAbstractWhenSourceIsAbstracttrueAn abstract source class produces an abstract DTO — you cannot accidentally instantiate half an object
GenerateToMethodsForAbstractfalseEmit the plain ToDto() methods (which use new) for abstract types. Off by default because new on an abstract type does not compile
GenerateFactoryOverloadsForAbstracttrueEmit ToDto(factory) overloads instead, so the caller supplies the concrete instance
AlwaysGenerateFactoryOverloadsfalseEmit the factory overloads for every type, not only abstract ones
csharp
// Abstract source
[AutoGenerateDto]
public abstract class EntityBase { public Guid Id { get; set; } }

// Generated: abstract DTO + a factory overload
var dto = entity.ToDto(() => new DerivedEntityDto());

On the attribute, AutoGenerateDerived = true additionally generates DTOs for every type derived from the annotated one, so a base class can opt its whole hierarchy in with one attribute. The sample demonstrates this with AnotherBaseWhereDerivedAreAutoGenerated.

Deep properties

DeepProperties (default false) controls inherited members. When true, properties of base classes that are not themselves generated are copied into the DTO — otherwise the DTO only carries its own declared properties and relies on a generated base DTO. Turn it on when your base types live in an assembly you cannot annotate.


Mapping generation

By default (GenerateMapping = true) the generator emits extension methods in both directions. Names are configurable per type:

csharp
[AutoGenerateDto(ToDtoMethodName = "ToMegaDto", ToSourceMethodName = "AsSrc")]
public class Address : EntityBase { … }
csharp
AddressDto dto = address.ToMegaDto();
Address back = dto.AsSrc();

DefaultMappingSettings sets the names once for the whole project:

json
"DefaultMappingSettings": {
  "MapWithClassMapper": true,
  "ToDtoMethodName": "ToDto",
  "ToSourceMethodName": "ToEntity"
}

MapWithClassMapper = true routes the property through Nextended.Core's class mapper instead of a direct assignment — which is what you want for a nested complex property whose DTO is generated separately.

Hooking into the mapping

Generated mapping code is mechanical, and sooner or later you need one field computed. Rather than editing generated output, opt into partial hooks:

csharp
[AutoGenerateDto(GenerateBeforeAndAfterAssignPartialsInMapping = true)]
public class Address : EntityBase { … }

The generator then emits partial method declarations that run before and after the property assignments. Implement them in your own partial class:

csharp
public partial class AddressDto
{
    partial void BeforeAssign(Address source) { … }
    partial void AfterAssign(Address source)
    {
        FullAddress = $"{Street} {Number}, {City}";
    }
}

GenerateBeforeAndAfterAssignPartialsInMappingAttribute applies the same thing at class level for sources you configure elsewhere. The sample keeps its hand-written partial next to the entities in Entities/Partials/AdressDto.cs.

Where the files land

PropertyDefaultEffect
OutputPathnullWhere DTOs go. null = added to the compilation, nothing on disk
MappingOutputPathnullSeparate destination for the mapping extension files
OneFilePerClasstrueOne file per generated type instead of one combined file
CreateFileHeaderstrueEmit the auto-generated header comment
CreateRegionstrueWrap sections in #region blocks
CreateCommentstrueCarry XML documentation over to the generated members
GeneratePartialtrueGenerate partial types so you can extend them

Writing to OutputPath puts the generated code in your repository rather than in obj/. That makes review diffs meaningful and lets you inspect what the generator actually produced — which is why the sample project checks its output in.


Walkthrough: the CodeGenSample project

Tests/TestProjects/CodeGenSample exercises all four generators and has every generated file committed, so you can read the input and the output side by side without running a build.

CodeGenSample/
├─ CodeGen.config.json            all four sections configured
├─ Entities/
│  ├─ Address.cs                  [AutoGenerateDto] with custom method names + COM
│  ├─ User.cs                     attribute retention, ignored members, renamed properties
│  ├─ AbstractClasses.cs          abstract + AutoGenerateDerived
│  ├─ GenericBaseClass.cs         generic sources
│  ├─ Base/                       EntityBase, interfaces
│  ├─ Enums/UserLevel.cs          enum → DTO enum
│  └─ Partials/AdressDto.cs       hand-written partial extending a generated DTO
├─ Sources/
│  ├─ appsettings.json            input for StructureGenerations
│  └─ scac_codes.xlsx             input for ExcelGenerations
└─ Generated/                     committed output
   ├─ Dtos/                       19 generated DTOs + ComGuids.g.cs
   ├─ Extensions/                 MappingExtensions.g.<Type>.cs, one per source type
   ├─ AppSettings/appsettings.g.cs
   ├─ Excel/scac_codes.row.g.cs + scac_codes.table.g.cs
   └─ HtmlDocs/                   CodeToDocs output

Wiring it up

The generator needs two things: the package reference, and the config file registered as an AdditionalFiles item so Roslyn hands it to the analyzer.

xml
<ItemGroup>
  <PackageReference Include="Nextended.Core" Version="10.1.22"
                    PrivateAssets="all" GeneratePathProperty="true" />
  <PackageReference Include="Nextended.CodeGen" Version="10.1.22" />
</ItemGroup>

<ItemGroup>
  <AdditionalFiles Include="CodeGen.config.json" />
</ItemGroup>

Nextended.Core carries the attributes. PrivateAssets="all" keeps it out of your own package's dependencies when the attributes are only used at build time.

Input → output, one property at a time

Entities/User.cs in the sample:

csharp
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
[AutoGenerateDto(Prefix = "My", Suffix = "Dto", IsComCompatible = false,
    KeepAttributesOnGeneratedClass = true,
    AddContainingNamespaceUsings = true,
    DefaultPropertyInterfaceAccess = InterfaceProperty.Get,
    KeepPropertyAttributesOnGeneratedClass = true,
    KeepPropertyAttributesOnGeneratedInterface = true)]
public class User : EntityBase
{
    [MaxLength(3)]
    public string Name { get; set; }

    [IgnoreOnGeneration]
    public string SecretDb { get; set; }

    [GenerationPropertySetting(PropertyName = "ThatUserAddress", MapWithClassMapper = true)]
    public Address Address { get; set; }

    [GenerationPropertySetting(PropertyName = "UserLevel", InterfaceAccess = InterfaceProperty.GetAndSet)]
    public UserLevel? Level { get; set; }
}

What each line does to the output — see Generated/Dtos/MyUserDto.g.cs:

InputResult
Prefix = "My", Suffix = "Dto"Type is named MyUserDto
KeepAttributesOnGeneratedClass[JsonNumberHandling(...)] is copied onto MyUserDto
KeepPropertyAttributesOnGeneratedClass[MaxLength(3)] survives on Name
[IgnoreOnGeneration] on SecretDbThe property does not exist on the DTO at all
PropertyName = "ThatUserAddress"Address is exposed as ThatUserAddress
MapWithClassMapper = trueThe nested Address is mapped by the class mapper, not assigned directly
DefaultPropertyInterfaceAccess = GetThe generated interface exposes get-only properties …
InterfaceAccess = GetAndSet on Level… except UserLevel, which is settable
AddContainingNamespaceUsingsA using for the source namespace is emitted

PropertiesToIgnore on the attribute is the alternative to [IgnoreOnGeneration] for source types you cannot edit.

Running it

bash
git clone https://github.com/fgilde/Nextended.git
cd Nextended/Tests/TestProjects/CodeGenSample
dotnet build

Because the sample writes to OutputPath, a build regenerates the files under Generated/. Run git diff afterwards to see exactly what the generator produced — an effective way to understand a config change, and a reasonable regression check to keep in CI.

Inspecting generation without an output path

When OutputPath is null, generated code goes into the compilation only. Make it visible with:

xml
<PropertyGroup>
  <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
  <CompilerGeneratedFilesOutputPath>GeneratedFiles</CompilerGeneratedFilesOutputPath>
</PropertyGroup>

Both lines are present, commented out, in the sample's .csproj.

Best Practices

1. Use Meaningful Names

csharp
[AutoGenerateDto(
    Namespace = "MyApp.Api.Dtos",
    ToDtoMethodName = "ToApiDto",
    ToSourceMethodName = "ToEntity"
)]
public class User { }

2. Organize Generated Code

json
{
  "DtoGeneration": {
    "Namespace": "MyApp.Generated.Dtos",
    "OutputPath": "./Generated/Dtos/",
    "OneFilePerClass": true
  }
}

3. Version Control Generated Files

Add to .gitignore if you regenerate on build, or commit them if you want to review changes.

4. Use Partial Classes for Customization

Generated as partial classes allow you to extend:

csharp
// Generated
public partial class UserDto
{
    public int Id { get; set; }
    public string Name { get; set; }
}

// Your custom code
public partial class UserDto
{
    public string DisplayName => $"User: {Name}";
}

Attribute Reference

AutoGenerateDtoAttribute

The AutoGenerateDtoAttribute is the primary attribute for generating DTOs from your classes. It provides extensive configuration options to control the generated code.

Properties

Namespace Configuration
csharp
[AutoGenerateDto(Namespace = "MyApp.Dtos")]
public class User { }

Namespace (string?)
Sets the namespace for the generated DTO. If not specified, uses the configuration file default or the source class namespace with a "Dto" suffix.

Naming Configuration
csharp
[AutoGenerateDto(
    Prefix = "Api",
    Suffix = "Response",
    GeneratedClassName = "CustomUserName"  // Overrides prefix/suffix
)]
public class User { }
// Generates: ApiUserResponse or CustomUserName

Prefix (string?)
Adds a prefix to the generated class name. Default: empty string.

Suffix (string?)
Adds a suffix to the generated class name. Default: "Dto".

GeneratedClassName (string?)
Explicitly sets the generated class name, overriding prefix and suffix logic.

Property Filtering
csharp
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    [IgnoreOnGeneration]
    public string Password { get; set; }  // Excluded from DTO
}

// Alternative: Configure in attribute
[AutoGenerateDto(PropertiesToIgnore = new[] { "Password", "InternalId" })]
public class User 
{
    public string Password { get; set; }
    public string InternalId { get; set; }
}

PropertiesToIgnore (string[])
Array of property names to exclude from the generated DTO.

Mapping Configuration
csharp
[AutoGenerateDto(
    GenerateMapping = true,
    ToDtoMethodName = "ToUserDto",
    ToSourceMethodName = "ToUser"
)]
public class User { }

// Usage
var user = GetUser();
var dto = user.ToUserDto();
var userAgain = dto.ToUser();

GenerateMapping (bool)
When true, generates extension methods for mapping between source and DTO. Default: true.

ToDtoMethodName (string?)
Name of the method that converts source to DTO. Default: "ToDto".

ToSourceMethodName (string?)
Name of the method that converts DTO back to source. Default: "ToSource".

COM Compatibility
csharp
[AutoGenerateDto(IsComCompatible = true)]
public class User { }

IsComCompatible (bool)
Generates COM-visible classes with appropriate attributes and GUIDs. Used for COM interop scenarios.

Type Configuration
csharp
[AutoGenerateDto(
    BaseType = "EntityBase",
    Interfaces = new[] { "IEntity", "IAuditable" }
)]
public class User { }

// Generates:
public class UserDto : EntityBase, IEntity, IAuditable { }

BaseType (string?)
Sets the base class for the generated DTO.

Interfaces (string[]?)
Adds interfaces that the generated DTO should implement.

Modifiers
csharp
[AutoGenerateDto(
    ClassModifier = Modifier.Internal,
    InterfaceModifier = Modifier.Public
)]
public class User { }

ClassModifier (Modifier)
Access modifier for the generated class. Options: Unset, Public, Private, Protected, Internal.

InterfaceModifier (Modifier)
Access modifier for the generated interface (if applicable).

DefaultPropertyInterfaceAccess (InterfaceProperty)
Default accessor type for interface properties. Options: Unset, GetAndSet, Get, Set.

Using Directives
csharp
[AutoGenerateDto(
    Usings = new[] { "System.Text.Json.Serialization", "MyApp.CustomTypes" },
    AddReferencedNamespacesUsings = true,
    AddContainingNamespaceUsings = true
)]
public class User { }

Usings (string[]?)
Additional using directives to include in the generated file.

AddReferencedNamespacesUsings (bool)
Automatically includes using directives for all referenced types.

AddContainingNamespaceUsings (bool)
Includes using directive for the source class's namespace.

Custom Code Injection
csharp
[AutoGenerateDto(
    PreInterfaceString = "[JsonObject]",
    PreClassString = "[Serializable]\n[DataContract]"
)]
public class User { }

// Generates:
[JsonObject]
public interface IUserDto { }

[Serializable]
[DataContract]
public class UserDto { }

PreInterfaceString (string?)
Code to insert before the generated interface declaration (e.g., attributes).

PreClassString (string?)
Code to insert before the generated class declaration (e.g., attributes).

Attribute Preservation
csharp
[AutoGenerateDto(
    KeepAttributesOnGeneratedClass = true,
    KeepAttributesOnGeneratedInterface = true,
    KeepPropertyAttributesOnGeneratedClass = true,
    KeepPropertyAttributesOnGeneratedInterface = true
)]
[DataContract]
public class User 
{
    [Required]
    [DataMember]
    public string Name { get; set; }
}

// Generated DTO keeps the attributes
[DataContract]
public class UserDto 
{
    [Required]
    [DataMember]
    public string Name { get; set; }
}

KeepAttributesOnGeneratedClass (bool)
Copies class-level attributes from source to generated class.

KeepAttributesOnGeneratedInterface (bool)
Copies class-level attributes from source to generated interface.

KeepPropertyAttributesOnGeneratedClass (bool)
Copies property-level attributes from source properties to generated class properties.

KeepPropertyAttributesOnGeneratedInterface (bool)
Copies property-level attributes from source properties to generated interface properties.

Derived Type Generation
csharp
[AutoGenerateDto(AutoGenerateDerived = true)]
public class BaseEntity { }

public class User : BaseEntity { }  // DTO automatically generated
public class Product : BaseEntity { }  // DTO automatically generated

AutoGenerateDerived (bool)
Automatically generates DTOs for all derived types when applied to a base class.

IgnoreOnGenerationAttribute

Use this attribute to exclude specific properties or fields from DTO generation.

csharp
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    [IgnoreOnGeneration]
    public string Password { get; set; }
    
    [IgnoreOnGeneration]
    public byte[] PasswordHash { get; set; }
}

GenerationPropertySettingAttribute

Fine-tune individual property generation with this attribute.

csharp
public class User
{
    public int Id { get; set; }
    
    [GenerationPropertySetting(
        PropertyName = "FullName",  // Rename in DTO
        MapWithClassMapper = true,   // Use ClassMapper for conversion
        InterfaceAccess = InterfaceProperty.Get,  // Read-only in interface
        PreClassString = "[JsonProperty(\"full_name\")]",
        KeepAttributesOnGeneratedClass = true
    )]
    public string Name { get; set; }
}

// Generated:
public interface IUserDto
{
    string FullName { get; }  // Read-only
}

public class UserDto : IUserDto
{
    [JsonProperty("full_name")]
    public string FullName { get; set; }
}

Properties:

  • PropertyName (string?) - Custom name for the property in the generated DTO
  • MapWithClassMapper (bool) - Use ClassMapper for type conversion during mapping
  • InterfaceAccess (InterfaceProperty) - Property accessor type in interfaces (Get, Set, or GetAndSet)
  • PreInterfaceString (string?) - Code to insert before the property in the interface
  • PreClassString (string?) - Code to insert before the property in the class
  • KeepAttributesOnGeneratedClass (bool) - Keep property attributes in generated class
  • KeepAttributesOnGeneratedInterface (bool) - Keep property attributes in generated interface

Complete Example: Advanced DTO Generation

csharp
using Nextended.Core.Attributes;
using Nextended.Core.Enums;

namespace MyApp.Domain
{
    /// <summary>
    /// User entity with complete DTO generation configuration
    /// </summary>
    [AutoGenerateDto(
        Namespace = "MyApp.Dtos",
        Suffix = "Response",
        ToDtoMethodName = "ToUserResponse",
        ToSourceMethodName = "ToUserEntity",
        GenerateMapping = true,
        BaseType = "AuditableDto",
        Interfaces = new[] { "IUserResponse", "IIdentifiable" },
        Usings = new[] { "System.Text.Json.Serialization" },
        ClassModifier = Modifier.Public,
        PreClassString = "[JsonSerializable(typeof(UserResponse))]",
        KeepPropertyAttributesOnGeneratedClass = true,
        PropertiesToIgnore = new[] { "PasswordHash", "PasswordSalt" }
    )]
    public class User
    {
        public int Id { get; set; }
        
        [Required]
        [MaxLength(100)]
        public string UserName { get; set; }
        
        [Required]
        [EmailAddress]
        public string Email { get; set; }
        
        [GenerationPropertySetting(
            PropertyName = "FullName",
            PreClassString = "[JsonPropertyName(\"fullName\")]"
        )]
        public string Name { get; set; }
        
        [IgnoreOnGeneration]
        public byte[] PasswordHash { get; set; }
        
        [IgnoreOnGeneration]
        public byte[] PasswordSalt { get; set; }
        
        public DateTime CreatedAt { get; set; }
        public DateTime? ModifiedAt { get; set; }
        
        public List<Address> Addresses { get; set; }
    }
    
    [AutoGenerateDto(
        Namespace = "MyApp.Dtos",
        Suffix = "Response"
    )]
    public class Address
    {
        public string Street { get; set; }
        public string City { get; set; }
        public string PostalCode { get; set; }
        public string Country { get; set; }
    }
}

Generated result:

csharp
namespace MyApp.Dtos
{
    using System;
    using System.Collections.Generic;
    using System.Text.Json.Serialization;
    using System.ComponentModel.DataAnnotations;
    
    [JsonSerializable(typeof(UserResponse))]
    public class UserResponse : AuditableDto, IUserResponse, IIdentifiable
    {
        [Required]
        [MaxLength(100)]
        public string UserName { get; set; }
        
        [Required]
        [EmailAddress]
        public string Email { get; set; }
        
        [JsonPropertyName("fullName")]
        public string FullName { get; set; }
        
        public DateTime CreatedAt { get; set; }
        public DateTime? ModifiedAt { get; set; }
        
        public List<AddressResponse> Addresses { get; set; }
    }
    
    public class AddressResponse
    {
        public string Street { get; set; }
        public string City { get; set; }
        public string PostalCode { get; set; }
        public string Country { get; set; }
    }
}

Usage:

csharp
var user = await _userRepository.GetByIdAsync(userId);
var response = user.ToUserResponse();
return Ok(response);

Troubleshooting

Generated Code Not Appearing

  1. Ensure CodeGen.config.json is marked as AdditionalFiles
  2. Clean and rebuild: dotnet clean && dotnet build
  3. Check build output for generator messages
  4. Verify source generators are enabled in IDE

Build Errors After Generation

  1. Check namespace conflicts
  2. Verify property types are valid
  3. Ensure all dependencies are installed

Supported Frameworks

  • .NET Standard 2.0 (Generator)
  • .NET 8.0+ (Generated code)

Dependencies

  • Roslyn APIs for source generation
  • Nextended.Core for attributes

Sample Project

See the CodeGenSample project for complete examples.