Mdk.DISourceGenerator 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Mdk.DISourceGenerator --version 1.0.0
NuGet\Install-Package Mdk.DISourceGenerator -Version 1.0.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="Mdk.DISourceGenerator" Version="1.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Mdk.DISourceGenerator --version 1.0.0
#r "nuget: Mdk.DISourceGenerator, 1.0.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.
// Install Mdk.DISourceGenerator as a Cake Addin
#addin nuget:?package=Mdk.DISourceGenerator&version=1.0.0

// Install Mdk.DISourceGenerator as a Cake Tool
#tool nuget:?package=Mdk.DISourceGenerator&version=1.0.0

Build and test Build, test, pack and publish

Summary

The DISourceGenerator package is designed to help clean up your service registration code when using the default Dependency Injection (DI) container in .NET. It allows you to use custom attributes to register your services, keeping the DI metadata close to the implementation classes.

  • Installation: The package is available on NuGet as Mdk.DISourceGenerator.
  • Attribute Usage: The package provides attributes like AddScoped, AddSingleton, and AddTransient for different lifetimes. You can use these attributes on your classes to register them with the DI container. For example, [AddScoped] class MyClass { ... } is equivalent to services.AddScoped<MyClass>();.
  • The incremental source generator translates the attributes to registration code for the default DI container.

DISourceGenerator

If you have a lot of services registered in the default DI container, your registration code can become some sort of a mess.

Using custom attributes can make your registration much cleaner. Attributes with registration information keep DI metadata close to the implementation classes the attributes are assigned to.

DISourceGenerator generates service registration source code based on attributes assigned to classes. Adding one line of code in ConfigureServices registers all services both in the host assembly as in directly and transitively referenced assemblies.

In the examples section of this repository a Blazor application and a Minimal API project are added, in which this registration strategy is implemented.

Installation

The source generator is available as a NuGet package: Mdk.DISourceGenerator

Attribute usage

Following examples focus on scoped registration. Use AddSingleton or AddTransient for other lifetimes.

Simple classes and interfaces

[AddScoped]
class MyClass { ... }

equals to services.AddScoped<MyClass>();

[AddScoped<IMyInterface>]
class MyClass: IMyInterface { ... }

equals to services.AddScoped<IMyInterface, MyClass>();

Generic attributes require C# 11. If you are still on a earlier version use [AddScoped(typeof(IMyInterface))]

Multiple attributes on one class
[AddScoped<IMyInterface1>]
[AddScoped<IMyInterface2>]
class MyClass: IMyInterface1, IMyInterface2 { ... }

equals to

services.AddScoped<IMyInterface1, MyClass>();
services.AddScoped<IMyInterface2, MyClass>();

Generic classes and interfaces

Unbound generic registration:
[AddScoped]
class MyClass<T> { ... }

equals to services.AddScoped(typeof(MyClass<>));

[AddScoped(typeof(IMyInterface<>))]
class MyClass<T>: IMyInterface<T> { ... }

equals to services.AddScoped(typeof(IMyInterface<>), typeof(MyClass<>));

Bound generic registration:
[AddScoped<MyClass<int>>]
class MyClass<T> { ... }

equals to services.AddScoped<MyClass<int>>();

[AddScoped<IMyInterface<int>>]
class MyClass<T>: IMyInterface<T> { ... }

equals to services.AddScoped<IMyInterface<int>, MyClass<int>>();

Multiple generic type parameters

Multiple generic type parameters are also supported, for example:

[AddScoped]
class MyClass<T, U> { ... }

equals to services.AddScoped(typeof(MyClass<,>));

Generated source

If there's a direct or transitive reference to the DISourceGenerator package and DIAttributes are used in an assembly, a registration method is generated per assembly.

This method registers all services in an assembly and also includes static method calls to all generated methods in referenced assemblies, both direct and transitive:

// <auto-generated />

using Microsoft.Extensions.DependencyInjection;

namespace Mdk.DISourceGenerator;

public static partial class DIRegistrations
{
    public static IServiceCollection RegisterServicesMinimalApi(this IServiceCollection services)
    {
        if (registeredServicesMinimalApi)
            return services;

        services.AddScoped<global::MinimalApi.MinimalApiService>();

        services.RegisterServicesBusinessBaseLogic();
        services.RegisterServicesBusinessLogic();

        registeredServicesMinimalApi = true;

        return services;
    }

    private static bool registeredServicesMinimalApi;
}

The naming convention for the static method is DIRegistrations.RegisterServices{AssemblyName}(...).

So in startup only one method call is needed to register all services based on DIAttributes:

using Mdk.DISourceGenerator;

builder.Services.RegisterServicesMinimalApi();

Inspecting generated source code

In the Solution Explorer of Visual Studio generated source can be found in:
{AssemblyName} > Dependencies > Analyzers > Mdk.DISourceGenerator > Mdk.DISourceGenerator.DISourceGenerator > DISourceGenerator.{AssemblyName}.g.cs

You can make the generated source more visible in your project and even add the generated files to source control. Copy and paste following PropertyGroup and ItemGroup into the .csproj-file.

<PropertyGroup>
  
  <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
  
  <CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
  
  <Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs" />
  
  <None Include="$(CompilerGeneratedFilesOutputPath)/**/*.cs" />
</ItemGroup>

This configuration is for all source generators in the project. Use git ignore to prevent output of (other) source generators to be included in source control.

References

Articles

Repositories

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

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
1.2.0 1,500 1/17/2024
1.1.0 997 1/9/2024
1.0.1 633 1/5/2024
1.0.0 634 1/4/2024