Devlooped.Extensions.DependencyInjection.Attributed 1.0.3

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

// Install Devlooped.Extensions.DependencyInjection.Attributed as a Cake Tool
#tool nuget:?package=Devlooped.Extensions.DependencyInjection.Attributed&version=1.0.3                

Automatic compile-time service registrations for Microsoft.Extensions.DependencyInjection with no run-time dependencies.

Usage

After installing the nuget package, by default a new [Service(ServiceLifetime)] attribute will be available to annotate your types:

[Service(ServiceLifetime.Scoped)]
public class MyService : IMyService, IDisposable
{
    public string Message => "Hello World";

    public void Dispose() { }
}

public interface IMyService 
{
    string Message { get; }
}

The ServiceLifetime argument is optional and defaults to ServiceLifetime.Singleton.

A source generator will emit (at compile-time) an AddServices extension method for IServiceCollection which you can call from your startup code that sets up your services, like:

var builder = WebApplication.CreateBuilder(args);

// Add discovered services to the container.
builder.Services.AddServices();
// ...

var app = builder.Build();

// Configure the HTTP request pipeline.
app.MapGet("/", (IMyService service) => service.Message);

// ...
app.Run();

NOTE: the service is available automatically for the scoped request.

And that's it. The source generator will discover annotated types in the current project and all its references too. Since the registration code is generated at compile-time, there is no run-time reflection (or dependencies) whatsoever.

How It Works

The generated code that implements the registration looks like the following:

services.AddScoped<MyService>();
services.AddScoped<IMyService>(s => s.GetRequiredService<MyService>());
services.AddScoped<IDisposable>(s => s.GetRequiredService<MyService>());

Note how the service is registered as scoped with its own type first, and the other two registrations just retrieve the same (according to its defined lifetime). This means the instance is reused and properly registered under all implemented interfaces automatically.

NOTE: you can inspect the generated code by setting EmitCompilerGeneratedFiles=true in your project file and browsing the generated subfolder under obj.

A linked source file named ServiceAttribute is also added to the project by the nuget package, but the source generator does not require it, since it matches the annotation by attribute name.

Advanced Scenarios

Your Own ServiceAttribute

If you want to declare your own ServiceAttribute and reuse from your projects, you can do it too by setting the following property in your project file:

<PropertyGroup>
    <IncludeServiceAttribute>false</IncludeServiceAttribute>
</PropertyGroup>

This will not add the attribute to the project's compilation. You can now create the attribute in your own shared library project like so:

[AttributeUsage(AttributeTargets.Class)]
public class ServiceAttribute : Attribute
{
    public ServiceAttribute(ServiceLifetime lifetime = ServiceLifetime.Singleton) { }
}

NOTE: since the constructor argument is only used by the source generation to detemine the registration style, but never at run-time, you don't even need to keep it around in a field or property!

With this in place, you only need to add the package to the top-level project that is adding the services to the collection!

Customize Generated Class

You can customize the generated class namespace and name with the following MSBuild properties:

<PropertyGroup>
    <AddServicesNamespace>MyNamespace</AddServicesNamespace>
    <AddServicesClassName>MyExtensions</AddServicesClassName>
</PropertyGroup>

They default to Microsoft.Extensions.DependencyInjection and AddServicesExtension respectively.

Sponsors

Clarius Org Christian Findlay C. Augusto Proiete Kirill Osenkov MFB Technologies, Inc. SandRock Eric C Andy Gocke

Sponsor this project  

Learn more about GitHub Sponsors

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
2.1.0-beta 62 11/12/2024
2.0.0 2,670 2/29/2024
1.3.2 7,133 12/13/2022
1.3.1 754 12/13/2022
1.3.0 792 12/6/2022
1.2.2 903 11/18/2022
1.2.1 857 11/16/2022
1.2.0 985 11/10/2022
1.1.3 781 10/31/2022
1.1.2 904 10/3/2022
1.1.1 869 10/3/2022
1.1.0 867 9/28/2022
1.0.3 895 9/27/2022