Skip to content

Source Generators

Source Generators


Source Generators

Source generators are a way to add source code to your solution during compilation.

SourceGenerators.png


Why

Source Generators allow you to add readily compiled code based on existing code.

This can help with producing optimized code without the need to do (slow) code inspection (aka Reflection) during runtime.

This can be used for

  • Compiling Razor pages
  • Generating optimized JSON (de-)serializers
  • Modifying existing code to add additional functionality (e.g. logging)

How to

  • Create a new project in which you want to use a source generator, e.g. a console application.

  • Create a second project. Choose class library as type. This is the project where your generation code goes.

  • Add the NuGet packages to the class library project:

    • Microsoft.CodeAnalysis.Analyzers
    • Microsoft.CodeAnalysis.CSharp

Create a class inside the class library:

using Microsoft.CodeAnalysis;
namespace SourceGenerator;
[Generator]
public class HelloSourceGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
// Build up the source code
string source = $@"// <auto-generated/>
using System;
namespace SourceGenerator
{{
public static class Hello
{{
public static void HelloFrom(string name) =>
Console.WriteLine($""Generator says: Hi from '{{name}}'"");
}}
}}
";
// Add the source code to the compilation
context.AddSource($"Hello.g.cs", source);
}
public void Initialize(GeneratorInitializationContext context)
{
// No initialization required for this one
}
}

Add the class library as a dependency to your console application, but modify the .csproj file to look like the following.

<ItemGroup>
<ProjectReference
Include="..\SgTest.Generator\SgTest.Generator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"
/>
</ItemGroup>

Now you should be able to use your generated code from within your console application, e.g. in Program.cs:

using SourceGenerator;
Hello.HelloFrom("Hugo");

Example: Swagger client generation

  • Create a console application
  • Add the NuGet package
    • H.Nswag.Generator
  • Add the file Petshop.nswag
{
"runtime": "Net80",
"defaultVariables": null,
"documentGenerator": {
"fromDocument": {
"json": "",
"url": "https://petstore.swagger.io/v2/swagger.json",
"output": null,
"newLineBehavior": "Auto"
}
},
"codeGenerators": {
"openApiToCSharpClient": {
"clientBaseClass": null,
"configurationClass": null,
"generateClientClasses": true,
"generateClientInterfaces": false,
"clientBaseInterface": null,
"injectHttpClient": true,
"disposeHttpClient": true,
"generateExceptionClasses": true,
"exceptionClass": "ApiException",
"wrapDtoExceptions": true,
"useHttpClientCreationMethod": false,
"httpClientType": "System.Net.Http.HttpClient",
"useHttpRequestMessageCreationMethod": false,
"useBaseUrl": true,
"generateBaseUrlProperty": true,
"generateSyncMethods": false,
"generatePrepareRequestAndProcessResponseAsAsyncMethods": false,
"exposeJsonSerializerSettings": false,
"clientClassAccessModifier": "public",
"typeAccessModifier": "public",
"generateContractsOutput": false,
"contractsNamespace": null,
"contractsOutputFilePath": null,
"parameterDateTimeFormat": "s",
"parameterDateFormat": "yyyy-MM-dd",
"generateUpdateJsonSerializerSettingsMethod": true,
"useRequestAndResponseSerializationSettings": false,
"serializeTypeInformation": false,
"queryNullValue": "",
"className": "{controller}PetshopApi",
"operationGenerationMode": "MultipleClientsFromOperationId",
"additionalNamespaceUsages": [],
"additionalContractNamespaceUsages": [],
"generateOptionalParameters": true,
"generateJsonMethods": false,
"enforceFlagEnums": false,
"parameterArrayType": "System.Collections.Generic.IEnumerable",
"parameterDictionaryType": "System.Collections.Generic.IDictionary",
"responseArrayType": "System.Collections.Generic.IReadOnlyCollection",
"responseDictionaryType": "System.Collections.Generic.IReadOnlyDictionary",
"wrapResponses": false,
"wrapResponseMethods": [],
"generateResponseClasses": true,
"responseClass": "SwaggerResponse",
"namespace": "Petshop.Apis",
"requiredPropertiesMustBeDefined": true,
"dateType": "System.DateTimeOffset",
"jsonConverters": null,
"anyType": "object",
"dateTimeType": "System.DateTimeOffset",
"timeType": "System.TimeSpan",
"timeSpanType": "System.TimeSpan",
"arrayType": "System.Collections.Generic.ICollection",
"arrayInstanceType": "System.Collections.ObjectModel.Collection",
"dictionaryType": "System.Collections.Generic.IDictionary",
"dictionaryInstanceType": "System.Collections.Generic.Dictionary",
"arrayBaseType": "System.Collections.ObjectModel.Collection",
"dictionaryBaseType": "System.Collections.Generic.Dictionary",
"classStyle": "Inpc",
"jsonLibrary": "SystemTextJson",
"generateDefaultValues": true,
"generateDataAnnotations": true,
"excludedTypeNames": [],
"excludedParameterNames": [],
"handleReferences": false,
"generateImmutableArrayProperties": false,
"generateImmutableDictionaryProperties": false,
"jsonSerializerSettingsTransformationMethod": null,
"inlineNamedArrays": false,
"inlineNamedDictionaries": false,
"inlineNamedTuples": true,
"inlineNamedAny": false,
"generateDtoTypes": true,
"generateOptionalPropertiesAsNullable": false,
"generateNullableReferenceTypes": true,
"templateDirectory": null,
"typeNameGeneratorType": null,
"propertyNameGeneratorType": null,
"enumNameGeneratorType": null,
"serviceHost": null,
"serviceSchemes": null,
"output": null,
"newLineBehavior": "Auto"
}
}
}
  • Notice all the options for client generation \o/
  • Add this ItemGroup to your .csproj file (your nswag file must be specified as an AdditionalFile for the client generator to be found)
<ItemGroup>
<AdditionalFiles
Include="Petshop.nswag" />
</ItemGroup>
  • Build your solution and use your generated client
using Petshop.Apis;
using var httpClient = new HttpClient();
var pets = await new PetshopApi(httpClient)
.FindPetsByStatusAsync([Anonymous.Available]);
foreach (var pet in pets)
{
Console.WriteLine(pet.Name);
}