AttributeDependencyInjection 1.0.0
dotnet add package AttributeDependencyInjection --version 1.0.0
NuGet\Install-Package AttributeDependencyInjection -Version 1.0.0
<PackageReference Include="AttributeDependencyInjection" Version="1.0.0" />
paket add AttributeDependencyInjection --version 1.0.0
#r "nuget: AttributeDependencyInjection, 1.0.0"
// Install AttributeDependencyInjection as a Cake Addin #addin nuget:?package=AttributeDependencyInjection&version=1.0.0 // Install AttributeDependencyInjection as a Cake Tool #tool nuget:?package=AttributeDependencyInjection&version=1.0.0
<img height="30px" src="./.assets/icon_64.png" alt="Attribute Dependency Injection"></img> Attribute Dependency Injection (DI)
This project provides a set of extensions to the IServiceCollection for registering services using custom attributes.
- Attribute-Based Injection: Register services by decorating classes and structs with custom attributes.
- Dynamic Service Registration: Allows methods decorated with special attributes to be invoked during the registration process.
- Exclusions: Exclude specific services or implementations from being registered.
How to Use
Add <code>DependencyAssemblyAttribute</code> for Discoverability
By default, only assemblies with the <code>DependencyAssemblyAttribute</code> will be scanned for attributed services (this behavior can be changed through the startup options).
.NET Core and later Add the attribute through the project's <code>.csproj</code> file:
<ItemGroup>
<AssemblyAttribute Include="AttributeDI.Attributes.DependencyAssemblyAttribute" />
</ItemGroup>
.NET Framework Add the attribute in the <code>AssemblyInfo.cs</code> file:
[assembly: DependencyAssembly]
Defining Services with Attributes
Services are defined using attributes that indicate how they should be registered with the DI container. The example below shows their basic usage:
using AttributeDI.Attributes;
using Microsoft.Extensions.DependencyInjection;
using System.ComponentModel;
namespace AttributedDI.ConsoleExample.Services
{
[ServiceRegistration] // Registers the FileService as a singleton service.
public sealed class FileService
{
public FileService()
{
}
}
public interface IResponderService
{
FileService Files { get; }
void Respond();
}
[ServiceRegistration(typeof(IResponderService))] // Registers the ResponderService as the implementation with
// IResponderService as the service type.
file sealed class ResponderService : IResponderService
{
public FileService Files { get; }
public ResponderService(FileService fs)
{
this.Files = fs;
}
public void Respond()
{
Console.WriteLine("Hello, World!");
}
}
public interface IQuestion
{
string Value { get; }
}
[DynamicServiceRegistration] // Invokes a method decorated with DynamicServiceRegistrationMethodAttribute.
public sealed class QuestionService
{
private readonly IQuestion _defaultQuestion;
internal QuestionService(IQuestion defaultQuestion)
{
_defaultQuestion = defaultQuestion;
}
public string Ask(IQuestion? question)
{
return question is null ? _defaultQuestion.Value : question.Value;
}
[DynamicServiceRegistrationMethod]
[EditorBrowsable(EditorBrowsableState.Never)]
private static void AddToServices(IServiceCollection services)
{
services.AddSingleton(x =>
{
var question = x.GetRequiredService<IQuestion>();
return new QuestionService(question);
});
}
[ServiceStructRegistration(typeof(IQuestion), Lifetime = ServiceLifetime.Transient)]
internal readonly struct Question : IQuestion
{
public string Value => "Hello, World!";
public Question()
{
}
}
}
[ServiceRegistration(typeof(IResponderService))]
public sealed class ExcludeThisService : IResponderService
{
public ExcludeThisService(FileService fs)
{
this.Files = fs;
}
public FileService Files { get; }
public void Respond()
{
throw new NotImplementedException();
}
}
}
Call the AddAttributedServices Method
To add the attributed services into your application's startup routine, use one of the overloads for <code>AttributeDI.AttributeDIExtensions.AddAttributedServices()</code>
using AttributeDI.Attributes;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
using Microsoft.Extensions.Configuration;
public class Startup
{
public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
// Register services using assemblies and configuration
services.AddAttributedServices(new[] { Assembly.GetExecutingAssembly() }, configuration);
// Or configure options with an action
services.AddAttributedServices(options =>
{
options.Configuration = configuration;
options.IncludeNonAttributedAssembliesInScan = true;
});
}
}
Excluding Services
During the initial registration setup, services can be excluded from being registered for any reason. Exclusions can be added by the service or the implementation type.
using AttributeDI.Attributes;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
using Microsoft.Extensions.Configuration;
public class Startup
{
public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
// Exclude service(s) for testing
services.AddAttributedServices(options =>
{
options.AddExclusions(x =>
{
x.Add<ExcludeThisService>()
.Add(typeof(TestCollection<>))
.Add<ITestInterface>();
});
});
}
}
Attributes
<code>ServiceRegistrationAttribute</code>
Indicates that the decorated class should be registered as a service. The lifetime of the service can be specified through the attribute property <code>Lifetime</code>.
// Registers a singleton service and implementation type of MyLoneService
[ServiceRegistration]
public sealed class MyLoneService
{
// Implementation
}
// -- or --
// Registers a service type 'IServiceType' with the implementation of MyService.
[ServiceRegistration(typeof(IServiceType), Lifetime = ServiceLifetime.Scoped)]
internal sealed class MyService : IServiceType
{
// Implementation
}
Generic types with this attribute will be registered with their generic type definition.
[ServiceRegistration(typeof(IServiceType<>), Lifetime = ServiceLifetime.Scoped)]
public sealed class MyService<T> : IServiceType<T>
{
// Implementation
}
<code>ServiceStructRegistrationAttribute</code>
Registers a struct implementation using the specified interface service type.
[ServiceStructRegistration(typeof(IServiceType), Lifetime = ServiceLifetime.Transient)]
public struct MyServiceStruct : IServiceType
{
// Implementation
}
<code>DynamicServiceRegistrationAttribute</code>
Indicates that a method decorated with the <code>DynamicServiceRegistrationMethodAttribute</code> should be invoked during the registration process to dynamically add services.
[DynamicServiceRegistration]
public sealed class DynamicService
{
private readonly IOtherService _other;
private DynamicService(IOtherService other)
{
_other = other;
}
[DynamicServiceRegistrationMethod]
private static void AddToServices(IServiceCollection services)
{
services.AddSingleton(x =>
{
var other = x.GetRequiredService<IOtherService>();
return new DynamicService(other);
});
}
}
Optionally, if the application's <code>IConfiguration</code> is provided to the startup options, you can specify a different overload:
[DynamicServiceRegistration]
public sealed class DynamicService
{
[DynamicServiceRegistrationMethod]
private static void AddToServices(IServiceCollection services, IConfiguration configuration)
{
services.AddOptions<DynamicService>()
.Bind(configuration);
}
}
Product | Versions 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 is compatible. 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. |
-
.NETStandard 2.0
- Microsoft.Bcl.HashCode (>= 1.1.1)
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.1)
- System.Memory (>= 4.5.5)
-
net8.0
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.0 | 103 | 7/28/2024 |