Depso 1.0.1

dotnet add package Depso --version 1.0.1
NuGet\Install-Package Depso -Version 1.0.1
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="Depso" Version="1.0.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Depso --version 1.0.1
#r "nuget: Depso, 1.0.1"
#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 Depso as a Cake Addin
#addin nuget:?package=Depso&version=1.0.1

// Install Depso as a Cake Tool
#tool nuget:?package=Depso&version=1.0.1

Depso Dependency Injection Source Generator

NuGet Depso AppVeyor

Depso is yet another source generator for dependency injection. However, it takes a different approach than existing source generators and uses a restricted subset of C# instead of using attributes to define dependencies. This leads to a more natural and readable code and allows for more extension points.

Behavior of the generated code is similar to the IServiceCollection interface in ASP.NET Core. For more information on how to use dependency injection, see the official documentation.

Examples of generated code can be found in the Test project.

Features

  • Clean and readable service registration code and generated code.
  • No reflection or runtime code generation.
  • Registering services with singleton, scoped, and transient lifetimes.
  • Registering services as multiple types.
  • Registering services using factory methods.
  • Compile-time validation of missing service registrations.
  • Resolving IEnumerable<T> services.
  • Disposing of services that implement IDisposable or IAsyncDisposable.
  • Grouping service registrations in modules and importing these registrations in service providers.

Usage

Add the Depso package to your project:

<ItemGroup>
    <PackageReference Include="Depso" Version="1.0.0" PrivateAssets="all" />
</ItemGroup>

Basic Usage

Services are registered by calling the AddSingleton, AddScoped, and AddTransient methods. It is possible to use the generic overloads or the non-generic overloads that accept a Type parameter. The end result is exactly the same. Services can be registered as their own type or as their base types or implemented interfaces.

using Depso;

public interface ITransientInterface { }
public class Singleton { }
public class Scoped { }
public class Transient : ITransientInterface { }
public class TypeOf { }

// Decorate the class with the ServiceProvider attribute.
[ServiceProvider]
public partial class Container
{
    // Declare the method to register services. Its signature must be
    // private void and it must be named as RegisterServices.
    private void RegisterServices()
    {
        // Register services by calling the AddSingleton, AddScoped, and AddTransient methods.
        AddSingleton<Singleton>();
        AddScoped<Scoped>();
        // Service can only be resolved as an ITransientInterface.
        AddTransient<ITransientInterface, Transient>();

        // Register using typeof expressions.
        AddTransient(typeof(TypeOf));
    }
}

Registering Services as Multiple Types

It is frequently needed to register a service as multiple types. Normally, this is done by registering the other types using factory methods and resolving the service in these methods using the passed IServiceProvider. This library provides AlsoAs and AlsoAsSelf methods to register a service as multiple types and reduce the boilerplate code.

using Depso;

public interface SingletonInterface { }
public interface ScopedInterface { }
public interface TransientInterface { }
public class Singleton : SingletonInterface { }
public class Scoped : ScopedInterface { }
public class Transient : TransientInterface { }

[ServiceProvider]
public partial class Container
{
    private void RegisterServices()
    {
        // Register a service as its own type and also as an interface.
        AddSingleton<Singleton>().AlsoAs<SingletonInterface>();
        AddScoped(typeof(Scoped)).AlsoAs(typeof(ScopedInterface));
        
        // Register a service as an interface and also as its own type.
        AddTransient<TransientInterface, Transient>().AlsoAsSelf();
    }
}

Factories

Factory methods can be used to create services manually or register existing instance objects. AddX methods have overloads that accept a factory method of type Func<IServiceProvider, T>. It is possible to use static methods, instance methods, and lambda expressions as factory methods.

using Depso;

public class Member { }
public class Lambda { }
public class Static { }
public class Instance { }

[ServiceProvider]
public partial class Container
{
    private readonly Member _member;

    public Container(Member member)
    {
        _member = member;
    }

    private void RegisterServices()
    {
        // Register an object instance.
        AddTransient(_ => _member);

        // Register a service using a lambda.
        AddTransient(_ => new Lambda());

        // Register a service using a static factory method.
        AddTransient(CreateStatic);

        // Register a service using an instance factory method.
        AddTransient(CreateInstance);
    }

    private static Static CreateStatic(IServiceProvider _) => new Static();
    private Instance CreateInstance(IServiceProvider _) => new Instance();
}

Modules

Modules can be used to group related services together. Modules can be nested and can reference other modules. Modules that are defined on other assemblies can also be imported. In this case, the types that are registered in the imported module must be accessible by the declaring service provider.

The RegisterServices method has to be static on modules. This means that modules can't use instance members.

using Depso;

public class ModuleFactory { }
public class ModuleService { }
public class OtherModuleService { }

[ServiceProviderModule]
public partial class Module
{
    private static void RegisterServices()
    {
        // Registration of services is the same as in the service provider.
        AddSingleton<ModuleService>();
        // Modules can import other modules.
        ImportModule<OtherModule>();
    }
}

[ServiceProviderModule]
public partial class OtherModule
{
    private static void RegisterServices()
    {
        AddSingleton<OtherModuleService>();
    }
}

[ServiceProvider]
public partial class Container
{
    private void RegisterServices()
    {
        ImportModule<Module>();
    }
}

Performance

The benchmark project is similar to the one in Jab project. However, all service providers are treated as IServiceProvider and the IServiceProvider interface is used to resolve services.

The differences between this library and Jab should probably be negligible when used in real-world applications.

Service Registration and First Resolve

BenchmarkDotNet v0.13.12, Windows 11 (10.0.22621.2134/22H2/2022Update/SunValley2)
Intel Core i9-10900K CPU 3.70GHz, 1 CPU, 20 logical and 10 physical cores
.NET SDK 8.0.202
  [Host]     : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2


| Method | Mean        | Error     | StdDev    | Ratio | RatioSD | Gen0   | Gen1   | Allocated | Alloc Ratio |
|------- |------------:|----------:|----------:|------:|--------:|-------:|-------:|----------:|------------:|
| MEDI   | 1,069.95 ns | 20.338 ns | 20.886 ns | 71.29 |    2.33 | 0.5188 | 0.0095 |    5432 B |       61.73 |
| Jab    |    15.86 ns |  0.207 ns |  0.194 ns |  1.06 |    0.03 | 0.0053 |      - |      56 B |        0.64 |
| Depso  |    15.02 ns |  0.322 ns |  0.331 ns |  1.00 |    0.00 | 0.0084 |      - |      88 B |        1.00 |

Service Resolution

BenchmarkDotNet v0.13.12, Windows 11 (10.0.22621.2134/22H2/2022Update/SunValley2)
Intel Core i9-10900K CPU 3.70GHz, 1 CPU, 20 logical and 10 physical cores
.NET SDK 8.0.202
  [Host]     : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2


| Method | Mean      | Error     | StdDev    | Ratio | RatioSD | Gen0   | Allocated | Alloc Ratio |
|------- |----------:|----------:|----------:|------:|--------:|-------:|----------:|------------:|
| MEDI   | 19.857 ns | 0.0458 ns | 0.0406 ns |  2.32 |    0.02 | 0.0023 |      24 B |        1.00 |
| Jab    | 11.937 ns | 0.0528 ns | 0.0468 ns |  1.39 |    0.01 | 0.0023 |      24 B |        1.00 |
| Depso  |  8.569 ns | 0.0651 ns | 0.0609 ns |  1.00 |    0.00 | 0.0023 |      24 B |        1.00 |
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in 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.0.1 95 4/12/2024
1.0.0 81 4/12/2024