Autoinjector 1.0.0

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

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

Autoinjector

Automatically inject services using attributes

There are several Nuget packages that will load services automatically. However, most of these do things that I don't like:

  1. loading services by default can lead to injecting services you don't want in DI. There are remedies for this, but loading by default won't tell you if you did something you didn't mean to do.
  2. loading services by namespace can lead to an unintuitive namespace structure just to make the DI framework happy.
  3. ignoring services by attribute is a solution to #1, but again, this is easy to overlook and there's no way to know if you forgot to ignore a service. This can load extra classes into DI if you don't ignore a service. This can also lead to a much larger attribute footprint in your app, dependening on your application structure, because you might need to suppress things like DTOs or data models.

I prefer to do things explicitly where possible, and automate where practical. By explicitly loading services, you still retain ultimate control over whether a service is injected or not. More to the point, you also get runtime feedback if you forget to register a service, whereas if services are loaded by default, you may forget to suppress loading classes into DI without error.

Installation

You can install Autoinjector by running the following command:

dotnet add package Autoinjector

Setup

There are two ways to set up Autoinjector.

If you're okay with a little manual work, you can cut down on some startup overhead by specifying which assemblies you want to scan for services.

var app = WebApplication.CreateBuilder(args);

app.Services.AutoinjectServicesFromAssembly(typeof(Program).Assembly);

The AutoinjectServicesFromAssembly() extension method returns the IServiceCollection, so you can chain multiple calls together if need be. Say you have two projects, one for your web layer and one for your service layer:

var app = WebApplication.CreateBuilder(args);

app.Services.AutoinjectServicesFromAssembly(typeof(MyService).Assembly)
            .AutoinjectServicesFromAssembly(typeof(Program).Assembly);

If you just want to autoinject and get it over with, you can scan all visible assemblies for services by using the AutoinjectServices() extension method:

var app = WebApplication.CreateBuilder(args);

app.Services.AutoinjectServices();

This method is not recommended. This will scan all visible assemblies looking for services to inject, leading to iteration over dozens of assemblies at startup. This will add unnecessary overhead to your startup process since only a few assemblies at most will actually have services.

But I ain't yer papa, so have at it, if you insist.

Registering services in DI

Now that you've set up your project to use Autoinjector, you can start injecting services. To do that, you decorate your service classes with [Service].

[Service] Attribute

The ServiceAttribute class has two public properties that we care about.

ServiceAttribute.Lifetime (default: ServiceLifetime.Scoped)

The ServiceAttribute.Lifetime property represents the lifetime of the service in DI. It is represented by the Microsoft.Extensions.DependencyInjection.ServiceLifetime enum, so it can accept any valid value on that enum.

ServiceAttribute.Types (default: null)

The ServiceAttribute.Types property represents an array of the Type(s) to register a service as in DI. If this array is null or empty, the service class is injected as its own type (i.e., MyService will be injected into DI as MyService). Otherwise, the service is injected into DI as each of the Types in the array.

Using [Service]

There are four different constructors for [Service], allowing you to fine-tune how you want to inject your service.

This constructor gives you the most control over how your service is injected. It allows you to specify the lifetime of the service, and also allows you to specify which Type(s) your service should be injected as.

Assume you're building a login system. You have two interfaces that help out with the password part of your login system: IPasswordHasher for hashing user passwords, and IPasswordValidator for validating that passwords meet your security requirements.

Now, imagine you have a DefaultPasswordValidator : IPasswordValidator and you want to register this in DI as a transient service of type IPasswordValidator. To do so:

using Autoinjector;

// Injected with Transient lifetime
// Injected as IPasswordValidator
[Service(ServiceLifetime.Transient, typeof(IPasswordValidator))]
public class DefaultPasswordValidator : IPasswordValidator
{
    // your code
}

Say this is a small app though, so you decide to put all your password-related functionality on a single class, PasswordManager. Now, you need to inject PasswordManager as both an IPasswordHasher and an IPasswordValidator:

using Autoinjector;

// Injected with Transient lifetime
// Injected as IPasswordHasher
// Injected as IPasswordValidator
[Service(ServiceLifetime.Transient, typeof(IPasswordHasher), typeof(IPasswordValidator))]
public class PasswordManager : IPasswordHasher, IPasswordValidator
{
    // your code
}
ServiceAttribute(ServiceLifetime)

If you want to inject a service as its own type and not an interface or base class (i.e. you want to be able to request a PasswordManager as a PasswordManager from DI), supply only the ServiceLifetime to the constructor:

using Autoinjector;

// Injected with Transient lifetime
// Injected as PasswordManager
[Service(ServiceLifetime.Transient)]
public class PasswordManager : IPasswordHasher, IPasswordValidator
{
    // your code
}
ServiceAttribute(params Type[])

If you want to save some typing, the ServiceAttribute.Lifetime property defaults to ServiceLifetime.Scoped, so you can omit it. To use the default scoped lifetime and inject a service as an array of Types, supply only the Type[] to the constructor:

using Autoinjector;

// Injected with Scoped lifetime
// Injected as IPasswordHasher
// Injected as IPasswordValidator
[Service(typeof(IPasswordHasher), typeof(IPasswordValidator))]
public class PasswordManager : IPasswordHasher, IPasswordValidator
{
    // your code
}
ServiceAttribute()

If you want your service to be scoped, and if you want to inject it as its own type, just use [Service] by itself:

using Autoinjector;

// Injected with Scoped lifetime
// Injected as PasswordManager
[Service]
public class PasswordManager : IPasswordHasher, IPasswordValidator
{
    // your code
}
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net6.0

    • No dependencies.

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 227 9/4/2022

Initial release