Vidyano.SourceGenerators 3.4.0

Prefix Reserved
dotnet add package Vidyano.SourceGenerators --version 3.4.0
                    
NuGet\Install-Package Vidyano.SourceGenerators -Version 3.4.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Vidyano.SourceGenerators" Version="3.4.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Vidyano.SourceGenerators" Version="3.4.0" />
                    
Directory.Packages.props
<PackageReference Include="Vidyano.SourceGenerators">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Vidyano.SourceGenerators --version 3.4.0
                    
#r "nuget: Vidyano.SourceGenerators, 3.4.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Vidyano.SourceGenerators@3.4.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Vidyano.SourceGenerators&version=3.4.0
                    
Install as a Cake Addin
#tool nuget:?package=Vidyano.SourceGenerators&version=3.4.0
                    
Install as a Cake Tool

Disclaimer

  • Use of SourceGenerators depend on available content.
  • Analyzers and CodeFixes are available in any project.

Some Patterns are not supported by Analyzers at the moment:

  • When Clause on Switch Expression / Statements

Generators

  • Actions Generator
  • BusinessRules Generator
  • Context Generator
  • CustomAction Generator
  • Index Generator
  • Model Generator
  • ProjectTargetContextType Generator
  • Recipients Generator
  • SplittedValueObjects Generator

Generated Constant classes

All generated Constant classes are available under {RootNamespace}.Service.Generated namespace.

  • BusinessRuleNames
  • PersistentObjectTypes
  • PersistentObjectAttributeNames
  • ProgramUnitNames
  • ProgramUnitItemsNames
  • QueryNames
  • QuerySources
  • ActionNames
  • Languages
  • MessageKeys and Messages
  • AppRoles
  • WebsiteNames
  • Obsolete.PersistentObjectTypes

Dependency Injection

By using the InitializeByCtor attribute on a field, the source generator will inject this field via the generated constructor.

The Attribute can only be applied when no constructor is provided.

public partial class CompanyActions
{
    [InitializeByCtor]
    private readonly IMyService myService;
}

Attribute is available via reference Vidyano.Abstractions.

Index Generator

Generate an index by adding the GenerateIndex attribute to an entity, a nested objects class within an entity, or any deeper nested structure.

Index Types

The generator supports two types of indexes:

1. Entity Indexes

For entities, this will generate:

  • A partial {Entity} class with a QueryType attribute.
  • A partial {VEntity} QueryType class.
  • A partial {Entities}_Overview Index class.
  • A V{Entities} property on the Context if it does not already exist.

Example:

[GenerateIndex]
public partial class Customer
{
    public string Id { get; set; }
    public string Name { get; set; }
}
2. Fan-out Indexes (Nested Collections)

For nested objects within entities, you can create fan-out indexes that flatten nested collections. These classes can optionally be marked with [ValueObject] but this is not required:

[GenerateIndex(typeof(Country), nameof(Country.Cities), nameof(City.Streets))]
[ValueObject(typeof(Country))]
public partial class Street
{
    [ValueKey]
    public string Id { get; set; }

    public string Name { get; set; }
}

This creates an index that fans out from CountryCitiesStreets, making it easy to query all streets across all cities in all countries.

Fan-out Index Requirements:

  • Must specify a root entity type (e.g., typeof(Country))
  • Must provide a path through collections (e.g., nameof(Country.Cities), nameof(City.Streets))
  • The final segment in the path must be the collection containing the target class

The generated context includes query methods for each level of the fan-out path:

public partial class ProjectContext
{
    // Query streets by city
    public IRavenQueryable<VStreet> City_Streets(CustomQueryArgs args)
    ...

    // Query streets by country
    public IRavenQueryable<VStreet> Country_Cities_Streets(CustomQueryArgs args)
    ...

    public IRavenQueryable<VStreet> VStreets
    ...
}
3. Fan-out Indexes with SplittedCollection

For value objects stored in separate documents using [SplittedCollection], the Index Generator automatically detects the splitted collection and uses the root entity's Id as the ObjectId prefix in the generated index:

public class CarChanges
{
    public string Id { get; set; } = null!;

    [Reference(typeof(Car))]
    public string Car { get; set; } = null!;

    [SplittedCollection(typeof(Car))]
    public List<CarChange> Changes { get; set; } = new();
}

[GenerateIndex(typeof(CarChanges), nameof(CarChanges.Changes))]
[ValueObject(typeof(CarChanges))]
public partial class CarChange
{
    [ValueKey]
    public string Id { get; set; } = null!;

    public string Column { get; set; } = null!;
    public string? OriginalValue { get; set; }
    public string? NewValue { get; set; }
}

This generates an index where the ObjectId is constructed using the Car reference (from the [SplittedCollection] attribute) instead of the CarChanges.Id, ensuring proper linking back to the root entity.

The generated context query method is named based on the splitted root type: Car_Changes instead of CarChanges_Changes:

public partial class ProjectContext
{
    // Query method named after splitted root (Car) + collection (Changes)
    public IRavenQueryable<VCarChange> Car_Changes(CustomQueryArgs args)
    ...

    public IRavenQueryable<VCarChange> VCarChanges =>
    ...
}

Important Notes:

  • Index will not be generated if QueryType or Index is already added manually, except if they are partial.
  • If the index contains an Id property, the IId interface will be applied
  • If the index contains any audit fields, the corresponding IAudit... interface will be applied

Additional Attributes

You can add several attributes to control the index:

On the Class
  • IndexReferenceProperty: When used at the class level, includes properties from the root entity or parent entities in the fan-out path for nested collection indexes.

    When used on a class:

    [GenerateIndex(typeof(Country), nameof(Country.Cities), nameof(City.Streets))]
    [IndexReferenceProperty(typeof(Country), "Id")]
    [IndexReferenceProperty(typeof(Country), "Name", Search = true)]
    [IndexReferenceProperty(typeof(City), nameof(City.Name))]
    public partial class Street { ... }
    

    Parameters:

    • First parameter: Type of the reference entity (e.g., typeof(Country))
    • Second parameter: Property path (e.g., nameof(Country.Name) or "Address.CityName")
    • Optional Search: Creates a second {Property}_Sort property to allow full search (only needed when values can contain spaces)
    • Optional IgnoreProperty: Include this property in the index mapping but do not sync to Vidyano
  • IndexCustomVariable: Define custom variables for use in the index Map function.

    [IndexCustomVariable("countryDetails", """LoadDocument<CountryDetail>(country.Id + "/details")""")]
    
  • IndexCustomWhere: Add custom where clauses to filter indexed documents.

    [IndexCustomWhere("countryDetails != null")]
    
  • IndexCustomProperty: Add custom computed properties to the index.

    [IndexCustomProperty("HasDetails", typeof(bool), "countryDetails != null", IgnoreProperty = true)]
    [IndexCustomProperty("InternalCode", typeof(string), "countryDetails.Code", Nullable = false)]
    

    Parameters:

    • Optional Nullable: Specifies if the property type is nullable (for reference types). For value types, use typeof(bool?) directly
    • Optional IgnoreProperty: Include this property in the index mapping but do not sync to Vidyano

    Note: For IndexCustomVariable and IndexCustomWhere, the order is important and will be reflected in the generated index.

On Properties
  • Search: Creates a second {Property}_Sort property to allow full search on this property.

    Note: This is only needed when values can contain spaces.

  • IgnoreForIndex: This property will not be included in the index.

  • IgnoreProperty: This attribute will be copied to the QueryType.

  • IndexReferenceProperty: Include properties from a reference in the index.

    When used on a property:

    [IndexReferenceProperty(nameof(Person.Name), Search = true)]
    [IndexReferenceProperty("Address.CityName")]
    [IndexReferenceProperty("InternalCode", IgnoreProperty = true)]
    [Reference(typeof(Person))]
    public string Person { get; set; }
    

    Parameters:

    • First parameter: Property path (e.g., nameof(Person.Name) or "Address.CityName")
    • Search: Creates a second {Property}_Sort property to allow full search (only needed when values can contain spaces)
    • IgnoreProperty: Include this reference property in the index mapping but do not sync to Vidyano

    Note:

    • The reference type is inferred from the [Reference] attribute
    • If no Reference attribute is found, you can specify the type as the first parameter on IndexReferenceProperty

Complete Example

[GenerateIndex(Index = typeof(Indexes.City_Overview))]
public partial class City
{
    public string Id { get; set; }

    [Search]
    public string Name { get; set; }

    [IgnoreForIndex]
    public string InternalNotes { get; set; }

    [IndexReferenceProperty(nameof(Country.Name), Search = true)]
    [IndexReferenceProperty(nameof(Country.Code), IgnoreProperty = true)]
    [Reference(typeof(Country))]
    public string Country { get; set; }

    public List<Street> Streets { get; set; }
}

[GenerateIndex(typeof(City), nameof(City.Streets))]
[IndexCustomVariable("cityDetails", """LoadDocument<CityDetail>(city.Id + "/details")""")]
[IndexCustomWhere("cityDetails != null")]
[IndexCustomProperty("HasCityDetails", typeof(bool), "cityDetails != null", Nullable = false)]
[IndexReferenceProperty(typeof(City), "Country.Name")]
[ValueObject(typeof(City))]
public partial class Street
{
    [ValueKey]
    public string Id { get; set; }

    public string Name { get; set; }

    [IndexReferenceProperty(nameof(House.Id), IgnoreProperty = true)]
    [Reference(typeof(House))]
    public string House { get; set; }
}

SplittedValueObjects Generator

Generate registration for splitted value objects by adding the SplittedCollection attribute to a collection property.

Add services.AddSplittedValueObjects() to register all splitted value objects.

Attribute syntax:

[SplittedCollection(typeof(RootType), suffix?: string)]

Parameters:

  • First parameter (required): typeof(RootType) - The root entity type
  • Second parameter (optional): suffix - Id suffix (e.g., "/invoiceLines")

Requirements:

  • Element type must be decorated with [ValueObject]
  • Containing class must have a [Reference] property of the RootType

Example:

public class CarChanges
{
    public string Id { get; set; } = null!;

    [Reference(typeof(Car))]
    public string Car { get; set; } = null!;

    [SplittedCollection(typeof(Car))]
    public List<CarChange> Changes { get; set; } = new();
}

Projects

Main project

Additional Files & Global Usings

Add following ItemGroup to the .csproj project file.


<ItemGroup>
  
  <AdditionalFiles Include="App_Data\**\*.json" />
  
  <Using Include="$(MSBuildProjectName).Service.Generated" />
  <Using Alias="Types" Include="$(MSBuildProjectName).Service.Generated.PersistentObjectTypes.$(MSBuildProjectName)" />
  <Using Alias="AttributeNames" Include="$(MSBuildProjectName).Service.Generated.PersistentObjectAttributeNames.$(MSBuildProjectName)" />
  <Using Alias="QueryNames" Include="$(MSBuildProjectName).Service.Generated.QueryNames.$(MSBuildProjectName)" />
  <Using Alias="QuerySources" Include="$(MSBuildProjectName).Service.Generated.QuerySources.$(MSBuildProjectName)" />
</ItemGroup>   

External Context

If the context file does not exist in the main project, you can use the appsettings.json file as an alternative, as the context will be read from there.


<ItemGroup>
  
  <AdditionalFiles Include="appsettings.json" />    
</ItemGroup>

Library project

Missing App_Data json files warning

When incorporating SourceGenerators into a library project, you may encounter a VIDYANO0010 warning indicating missing App_Data JSON files. This warning is not applicable to library projects, as they do not utilize App_Data.

To bypass this warning, two methods are available.

is_global = true
disable_model_source_generator = true
  • By ignoring the warning via csproj file
<NoWarn>VIDYANO0010</NoWarn>

EditorConfig Settings

When your project or library does not follow the default naming conventions, you can configure the source generators via .editorconfig file.

Create or update your .editorconfig file with the following settings:

is_global = true

# Global Settings (apply to all generators)
source_generator_namespaces = MyNamespace, AnotherNamespace
source_generator_excluded_namespaces = MyNamespace.Internal

# Per-Generator Settings
# Disable specific generators
disable_model_source_generator = true
disable_actions_source_generator = true
disable_businessrules_source_generator = true
disable_context_source_generator = true
disable_customaction_source_generator = true
disable_index_source_generator = true
disable_projecttargetcontexttype_source_generator = true
disable_recipients_source_generator = true
disable_splittedvalueobjects_source_generator = true

# Per-generator namespace configuration (overrides global settings)
model_source_generator_namespaces = MyNamespace
model_source_generator_excluded_namespaces = MyNamespace.Internal

Available Settings

Setting Description
source_generator_namespaces Comma-separated list of namespaces to include (global)
source_generator_excluded_namespaces Comma-separated list of namespaces to exclude (global)
disable_{generator}_source_generator Set to true to disable a specific generator
{generator}_source_generator_namespaces Namespaces to include for a specific generator
{generator}_source_generator_excluded_namespaces Namespaces to exclude for a specific generator

Notes:

  • Excluded namespaces are typically used to omit sub-namespaces from an included parent namespace (e.g., including MyNamespace but excluding MyNamespace.Internal).
  • When overriding default excluded namespaces, be aware that source generator behavior can change. For example, all generators except customaction should exclude the custom actions namespace (e.g., {RootNamespace}.Service.CustomActions) to function as expected.

Generator Names

Use these names in the settings (lowercase):

  • model - Model Generator
  • actions - Actions Generator
  • businessrules - BusinessRules Generator
  • context - Context Generator
  • customaction - CustomAction Generator
  • index - Index Generator
  • projecttargetcontexttype - ProjectTargetContextType Generator
  • recipients - Recipients Generator
  • splittedvalueobjects - SplittedValueObjects Generator

Default Behavior

By default, generators scan:

  • The root namespace of your project
  • The CronosCore namespace

And exclude:

  • Microsoft.*, System.*, JetBrains.*, Raven.* namespaces (always excluded)
  • {RootNamespace}.Service.CustomActions namespace

Exceptions

When using EmitCompilerGeneratedFiles in .csproj project file you can get exceptions when compiling in Windows because of use of long filenames.

<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>

To fix this issue you need to add the following registration key (Reboot needed to take effect)

reg ADD HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem /v LongPathsEnabled /t REG_DWORD /d 1

Backwards Compatibility

For backwards compatibility we created an Obsolete PersitentObjectTypes class (old version). The only thing you need todo when upgrading to the new source generator is changing the using in de .csproj project file by adding the Generated.Obsolete namespace.

<Using Alias="Types" Include="Fleet.Service.Generated.Obsolete.PersistentObjectTypes.Fleet" />

This way the project wil run as before.

Note: If you add the new usings directly you can use de CodeFix to update to the correct code.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.4.0 579 12/10/2025
3.3.0 745 12/3/2025
3.2.0 421 11/24/2025
3.1.0 561 11/18/2025
3.0.0 364 11/7/2025
2.23.0 1,640 9/19/2025
2.22.1 2,296 5/16/2025
2.22.0 391 5/13/2025
2.21.2 1,894 3/12/2025
2.21.1 243 3/12/2025
2.21.0 513 3/5/2025
2.19.1 1,451 1/23/2025
2.19.0 549 1/16/2025
2.18.0 240 1/16/2025
2.16.0 1,690 12/4/2024
2.15.0 1,101 11/19/2024
2.14.0 1,698 10/18/2024
2.13.0 1,022 10/2/2024
2.12.0 478 9/16/2024
2.11.0 388 8/30/2024
2.10.0 1,694 7/7/2024
2.9.0 932 6/27/2024
2.8.0 1,688 5/26/2024
2.7.0 917 5/7/2024
2.6.0 823 4/19/2024
2.5.1 1,048 3/29/2024
2.4.0 1,116 2/29/2024
2.3.0 380 2/22/2024
2.2.3 2,914 10/23/2023
2.1.6 1,759 9/19/2023
1.8.1 1,676 6/29/2023
1.8.0 301 6/28/2023
1.7.0 376 6/25/2023
1.6.2 800 6/12/2023
1.6.1 875 5/17/2023
1.6.0 283 5/17/2023
1.5.3 721 5/2/2023
1.5.2 1,092 4/1/2023
1.5.1 406 3/31/2023
1.4.1 1,039 2/17/2023