Auto-Register
9.0.100
dotnet add package Auto-Register --version 9.0.100
NuGet\Install-Package Auto-Register -Version 9.0.100
<PackageReference Include="Auto-Register" Version="9.0.100" />
paket add Auto-Register --version 9.0.100
#r "nuget: Auto-Register, 9.0.100"
// Install Auto-Register as a Cake Addin #addin nuget:?package=Auto-Register&version=9.0.100 // Install Auto-Register as a Cake Tool #tool nuget:?package=Auto-Register&version=9.0.100
Auto-Register
Overview
The Auto-Register package simplifies service registration for ASP.NET Core applications by automatically discovering and registering services based on custom attributes. This package eliminates the need for manually adding services in Program.cs, supports multiple service lifetimes, and ensures no duplicate registrations occur.
With Auto-Register, services are identified using the RegisterAttribute and are automatically registered as self, interface, or base class implementations.
Installation
To install Auto-Register, add it to your project using the NuGet Package Manager or .NET CLI:
Using Package Manager:
Install-Package Auto-Register
Using .NET CLI:
dotnet add package Auto-Register
Usage
Once the Auto-Register package is installed, you can easily use it in your ASP.NET Core project to auto-register services.
Step 1: Mark Services with RegisterAttribute
Services that need to be registered must be decorated with the RegisterAttribute. This attribute takes the ServiceLifetime (Singleton, Scoped, or Transient) as a parameter.
Example:
using AutoRegister;
// Singleton service
[Register(ServiceLifetime.Singleton)]
public class MySingletonService : IMySingletonService
{
// Implementation
}
// Scoped service
[Register(ServiceLifetime.Scoped)]
public class MyScopedService : IMyScopedService
{
// Implementation
}
// Transient service
[Register(ServiceLifetime.Transient)]
public class MyTransientService : IMyTransientService
{
// Implementation
}
Step 2: Register Services in Program.cs
In your ASP.NET Core application, use the AddAutoregister extension method to automatically register services from a given assembly.
If using ASP.NET Core 5.0+ with a minimal hosting model (Program.cs), add the auto-registration in the ConfigureServices section
using AutoRegister;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// Automatically register services marked with the RegisterAttribute
builder.Services.AddAutoregister(Assembly.GetExecutingAssembly());
var app = builder.Build();
app.Run();
Example Scenario
Consider an ASP.NET Core application where you want to automatically register several services:
[Register(ServiceLifetime.Singleton)]
public class AuthService : IAuthService
{
// Singleton service for authentication
}
[Register(ServiceLifetime.Scoped)]
public class ShoppingCartService : IShoppingCartService
{
// Scoped service for managing shopping carts
}
[Register(ServiceLifetime.Transient)]
public class PaymentService : IPaymentService
{
// Transient service for handling payments
}
// Program.cs (ASP.NET Core 5.0+)
var builder = WebApplication.CreateBuilder(args);
// Automatically register services
builder.Services.AddAutoregister(Assembly.GetExecutingAssembly());
var app = builder.Build();
app.Run();
In this example:
AuthService will be registered as a Singleton
ShoppingCartService will be registered as a Scoped
PaymentService will be registered as a Transient
The services will automatically be resolved and injected where required, without needing to manually specify them in Program.cs
Components
RegisterAttribute
This attribute is used to mark classes for automatic registration. It defines the lifetime of the service (Singleton, Scoped, or Transient) via the constructor.
Constructor:
public RegisterAttribute(ServiceLifetime lifetime)
Parameter:
ServiceLifetime lifetime: Specifies the lifetime of the service to be registered (Singleton, Scoped, or Transient)
Example:
[Register(ServiceLifetime.Singleton)]
public class MyService : IMyService
{
// Implementation
}
Key Features
1. Automatic Service Discovery and Registration:
Services marked with the RegisterAttribute are automatically discovered and registered based on their lifetime (Singleton, Scoped, or Transient)
2. Interface and Base Class Registration:
Classes can be registered not only as themselves but also as their interfaces, abstract or any base class.
3. Self-Registration:
Classes that do not implement interfaces or inherit from abstract base classes can still be self-registered in the service collection.
4. Duplicate Prevention:
Services are registered only once, preventing multiple registrations of the same type.
5. Lifetime Control:
Service lifetime is controlled via the RegisterAttribute, making it easy to specify whether a service should be Singleton, Scoped, or Transient
Advanced Usage
Registering External Assemblies
If you want to register services from multiple assemblies, you can pass those assemblies to the AddAutoregister method. For example:
using AutoRegister;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// Register services from multiple assemblies
builder.Services.AddAutoregister(AppDomain.CurrentDomain.GetAssemblies());
var app = builder.Build();
app.Run();
Ignoring Specific Services
Currently, all services marked with the RegisterAttribute in the provided assemblies will be registered. If you want to exclude certain services, you would need to manually intervene before the registration process.
Supported Platforms
Auto-Register is designed for use any types of .NET applications that support Microsoft Dependency Injection, including:
- ASP.NET Core Applications
- Worker Services
- Blazor Applications
- WPF Applications
- Windows Forms Applications
I am showing some examples through a console application
Advanced Generic Service Processing with Auto Registration in ASP.NET Core
using AutoRegister;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
var services = new ServiceCollection();
services.AddAutoregister(Assembly.GetExecutingAssembly());
using var serviceProvider = services.BuildServiceProvider();
var processor = serviceProvider.GetRequiredService<IMyProcess>();
processor.Save(new Customer());
public interface IEntity
{
Guid Id { get; }
}
public interface IRepository<T> where T : IEntity
{
void Save(T entity);
}
[Register(ServiceLifetime.Transient)]
public class Repository<T> : IRepository<T> where T : IEntity, new()
{
public void Save(T entity)
{
Console.WriteLine($"Entity with ID {entity.Id} has been saved.");
}
}
public interface IService<T> where T : IEntity
{
void Process(T entity);
}
[Register(ServiceLifetime.Transient)]
public class Service<T> : IService<T> where T : IEntity, new()
{
private readonly IRepository<T> _repository;
public Service(IRepository<T> repository)
{
_repository = repository;
}
public void Process(T entity)
{
Console.WriteLine($"Processing entity with ID {entity.Id}...");
_repository.Save(entity);
}
}
public class Customer : IEntity
{
public Guid Id { get; private set; }
public Customer()
{
Id = Guid.NewGuid();
}
}
[Register(ServiceLifetime.Transient)]
public class Processor<TRepository, TService, TEntity>
where TRepository : IRepository<TEntity>
where TService : IService<TEntity>
where TEntity : IEntity, new()
{
private readonly TRepository _repository;
private readonly TService _service;
public Processor(TRepository repository, TService service)
{
_repository = repository;
_service = service;
}
public void Execute()
{
TEntity entity = new TEntity();
Console.WriteLine($"Executing for entity with ID {entity.Id}...");
_service.Process(entity);
}
}
[Register(ServiceLifetime.Transient)]
public class AdvanceProcess : Processor<Repository<Customer>, Service<Customer>, Customer>, IMyProcess
{
public AdvanceProcess(Repository<Customer> repository, Service<Customer> service)
: base(repository, service)
{
}
public void Save(Customer entity)
{
Console.WriteLine($"Entity with ID {entity.Id} and the type is {entity.GetType().FullName}");
}
}
public interface IMyProcess
{
void Save(Customer entity);
}
[Output] : Entity with ID 3ccac989-03bc-4a17-9ae1-ff444140b185 and the type is Customer
Here are a few more examples that expand on the Advanced Generic Service Processing model with auto-registration and dependency injection in ASP.NET Core.
Example 1: Simple Open Generic Interface and Class
using AutoRegister;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
var services = new ServiceCollection();
services.AddAutoregister(Assembly.GetExecutingAssembly());
using var serviceProvider = services.BuildServiceProvider();
var myService = serviceProvider.GetRequiredService<IMyService<int>>();
myService.DoExecute();
public interface IMyService<T>
{
void DoExecute();
}
[Register(ServiceLifetime.Scoped)]
public class MyService<T> : IMyService<T>
{
public void DoExecute()
{
Console.WriteLine($"Executed Scoped Service with type {typeof(T)}");
}
}
[Output] : Executed Scoped Service with type System.Int32
Example 2: Closed Generic Interface and Class
using AutoRegister;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
var services = new ServiceCollection();
services.AddAutoregister(Assembly.GetExecutingAssembly());
using var serviceProvider = services.BuildServiceProvider();
var myService = serviceProvider.GetRequiredService<IClosedGenericService<int>>();
myService.Process(99);
public interface IClosedGenericService<T>
{
void Process(T type);
}
[Register(ServiceLifetime.Scoped)]
public class ClosedGenericService : IClosedGenericService<int>
{
public void Process(int type)
{
Console.WriteLine($"Processing Closed Generic Service with int {type}");
}
}
[Output] : Processing Closed Generic Service with int 99
Example 3: Multiple Generic Types
using AutoRegister;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
var services = new ServiceCollection();
services.AddAutoregister(Assembly.GetExecutingAssembly());
using var serviceProvider = services.BuildServiceProvider();
var myService = serviceProvider.GetRequiredService<IMyService<int, string>>();
myService.DoExecute(5, "Auto-Register");
public interface IMyService<T1, T2>
{
void DoExecute(T1 type1, T2 type2);
}
[Register(ServiceLifetime.Transient)]
public class MyService : IMyService<int, string>
{
public void DoExecute(int type1, string type2)
{
Console.WriteLine($"Executed Transient with {type1} and {type2}");
}
}
[Output] : Executed Transient with 5 and Auto-Register
Example 4: Non-Generic Interface and Class
using AutoRegister;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
var services = new ServiceCollection();
services.AddAutoregister(Assembly.GetExecutingAssembly());
using var serviceProvider = services.BuildServiceProvider();
var myService = serviceProvider.GetRequiredService<IMyService>();
myService.DoExecute();
public interface IMyService
{
void DoExecute();
}
[Register(ServiceLifetime.Singleton)]
public class MyService : IMyService
{
public void DoExecute()
{
Console.WriteLine("Non-generic service executed (Singleton)");
}
}
[Output] : Non-generic service executed (Singleton)
Example 5: Closed Generic Class with Concrete Types
using AutoRegister;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
var services = new ServiceCollection();
services.AddAutoregister(Assembly.GetExecutingAssembly());
using var serviceProvider = services.BuildServiceProvider();
var myService = serviceProvider.GetRequiredService<IMyService>();
myService.DoExecute();
public interface IMyService
{
void DoExecute();
}
[Register(ServiceLifetime.Scoped)]
public class MyService : MyGenericService<int>, IMyService
{
}
[Register(ServiceLifetime.Scoped)]
public class MyGenericService<T>
{
public void DoExecute()
{
Console.WriteLine($"Executed with closed generic type: {typeof(T)}");
}
}
[Output] : Executed with closed generic type: System.Int32
Example 6: Complex Generic Class with Constraints
using AutoRegister;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
var services = new ServiceCollection();
services.AddAutoregister(Assembly.GetExecutingAssembly());
using var serviceProvider = services.BuildServiceProvider();
var myService = serviceProvider.GetRequiredService<IMyService<int>>();
myService.DoExecute();
public interface IMyService<T> where T : struct
{
void DoExecute();
}
[Register(ServiceLifetime.Singleton)]
public class MyService<T> : IMyService<T> where T : struct
{
public void DoExecute()
{
Console.WriteLine($"Executed Singleton with {typeof(T)}");
}
}
[Output] : Executed Singleton with System.Int32
Example 7: Generic with Multiple Constraints
using AutoRegister;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
var services = new ServiceCollection();
services.AddAutoregister(Assembly.GetExecutingAssembly());
using var serviceProvider = services.BuildServiceProvider();
var myService = serviceProvider.GetRequiredService<IMyService<int>>();
myService.DoExecute();
public interface IMyService<T> where T : struct, IComparable
{
void DoExecute();
}
[Register(ServiceLifetime.Scoped)]
public class MyService<T> : IMyService<T> where T : struct, IComparable
{
public void DoExecute()
{
Console.WriteLine($"Executed Scoped with {typeof(T)} and constraint");
}
}
[Output] : Executed Scoped with System.Int32 and constraint
Example 8: Nested Generics
using AutoRegister;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
var services = new ServiceCollection();
services.AddAutoregister(Assembly.GetExecutingAssembly());
using var serviceProvider = services.BuildServiceProvider();
var myService = serviceProvider.GetRequiredService<IMyService<List<int>>>();
myService.DoExecute();
public interface IMyService<T>
{
void DoExecute();
}
[Register(ServiceLifetime.Transient)]
public class MyService<T> : IMyService<T>
{
public void DoExecute()
{
Console.WriteLine($"Executed Transient with nested {typeof(T)}");
}
}
[Output] : Executed Transient with nested System.Collections.Generic.List`1[System.Int32]
Example 9: Hybrid Non-Generic and Generic Services
using AutoRegister;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
var services = new ServiceCollection();
services.AddAutoregister(Assembly.GetExecutingAssembly());
using var serviceProvider = services.BuildServiceProvider();
var myService = serviceProvider.GetRequiredService<HybridService>();
myService.DoExecute<int>();
[Register(ServiceLifetime.Scoped)]
public class HybridService
{
private readonly OpenGenericService<int> _openGenericService;
public HybridService(OpenGenericService<int> openGenericService)
{
_openGenericService = openGenericService;
}
public void DoExecute<T>()
{
Console.WriteLine("Executing Scoped HybridService.");
_openGenericService.DoExecute();
}
}
[Register(ServiceLifetime.Scoped)]
public class OpenGenericService<T>
{
public void DoExecute()
{
Console.WriteLine($"Executing Scoped OpenGenericService with type: {typeof(T)}.");
}
}
[Output] :
Executing Scoped HybridService.
Executing Scoped OpenGenericService with type: System.Int32.
Example 10: Multi-Layer Inheritance (Non-Generic and Generic)
using AutoRegister;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
var services = new ServiceCollection();
services.AddAutoregister(Assembly.GetExecutingAssembly());
using var serviceProvider = services.BuildServiceProvider();
var myService = serviceProvider.GetRequiredService<LayeredService>();
myService.DoExecute<int>();
public abstract class BaseService
{
public abstract void DoExecute<T>();
}
[Register(ServiceLifetime.Singleton)]
public class GenericBaseService<U> : BaseService
{
public override void DoExecute<T>()
{
Console.WriteLine("Executing Singleton GenericBaseService with type: " + typeof(T));
}
}
[Register(ServiceLifetime.Singleton)]
public class LayeredService : GenericBaseService<int>
{
public override void DoExecute<T>()
{
Console.WriteLine("Executing Singleton LayeredService.");
base.DoExecute<T>();
}
}
[Output] :
Executing Singleton LayeredService.
Executing Singleton GenericBaseService with type: System.Int32
Example 11: Multiple Implementations of Interface Non-Generic and Generic Services
using AutoRegister;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
var services = new ServiceCollection();
services.AddAutoregister(Assembly.GetExecutingAssembly());
using var serviceProvider = services.BuildServiceProvider();
var myService = serviceProvider.GetRequiredService<IServiceFactory>()
.GetService("A");
myService.DoExecute<string>();
public interface IService
{
void DoExecute<T>();
}
[Register(ServiceLifetime.Scoped)]
public class ServiceImplementationA : IService
{
public void DoExecute<T>()
{
Console.WriteLine($"Executing Scoped ServiceImplementationA with type: {typeof(T)}.");
}
}
[Register(ServiceLifetime.Scoped)]
public class ServiceImplementationB : IService
{
public void DoExecute<T>()
{
Console.WriteLine($"Executing Scoped ServiceImplementationB with type: {typeof(T)}.");
}
}
[Register(ServiceLifetime.Scoped)]
public class ServiceFactory : IServiceFactory
{
private readonly IServiceProvider _serviceProvider;
public ServiceFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IService GetService(string implementationType)
{
return implementationType switch
{
"A" => _serviceProvider.GetRequiredService<ServiceImplementationA>(),
"B" => _serviceProvider.GetRequiredService<ServiceImplementationB>(),
_ => throw new ArgumentException($"Service type {implementationType} is not supported.")
};
}
}
public interface IServiceFactory
{
IService GetService(string implementationType);
}
[Output] : Executing Scoped ServiceImplementationA with type: System.String.
Example 12: Complex Generic Class with inheritance and interface implementation
using AutoRegister;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
var services = new ServiceCollection();
services.AddAutoregister(Assembly.GetExecutingAssembly());
using var serviceProvider = services.BuildServiceProvider();
var myCar = serviceProvider.GetRequiredService<ElectricCar>();
myCar.Drive();
[Register(ServiceLifetime.Scoped)]
public class Vehicle
{
public virtual void StartEngine()
{
Console.WriteLine("Engine started.");
}
}
public abstract class LandVehicle : Vehicle
{
public abstract void Drive();
}
public interface ITransport
{
void GetTransportMode();
}
[Register(ServiceLifetime.Scoped)]
public class Car<T> : LandVehicle, ITransport where T : Vehicle, new()
{
public override void Drive()
{
Console.WriteLine("Driving on the road.");
}
public void GetTransportMode()
{
Console.WriteLine("Land transport");
}
public override void StartEngine()
{
Console.WriteLine("Car engine started.");
}
}
[Register(ServiceLifetime.Scoped)]
public class ElectricCar : Car<ElectricCar>
{
public override void Drive()
{
Console.WriteLine("Driving silently on electric power.");
}
}
[Output] : Driving silently on electric power.
Conclusion
The Auto-Register NuGet package provides a powerful and flexible way to manage service registration in ASP.NET Core. By automating service discovery and registration, it reduces boilerplate code and helps maintain clean and maintainable service registration logic, especially in large projects with many services.
Product | Versions 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. |
-
net6.0
- Microsoft.Extensions.DependencyInjection (>= 9.0.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 |
---|---|---|
9.0.100 | 58 | 11/26/2024 |
8.0.6 | 99 | 10/8/2024 |
8.0.5 | 86 | 10/8/2024 |
8.0.4 | 77 | 10/8/2024 |
8.0.3 | 83 | 10/8/2024 |
8.0.2 | 86 | 10/8/2024 |
8.0.1 | 82 | 10/8/2024 |
7.8.5 | 90 | 10/5/2024 |
7.7.1 | 86 | 10/4/2024 |
7.6.2 | 91 | 10/4/2024 |
7.5.2 | 84 | 10/3/2024 |
7.3.1 | 91 | 10/2/2024 |
7.2.2 | 89 | 10/2/2024 |
7.2.1 | 88 | 9/30/2024 |
7.1.2 | 74 | 9/30/2024 |
6.0.6 | 76 | 9/30/2024 |
6.0.5 | 79 | 9/28/2024 |
6.0.4 | 84 | 9/28/2024 |
5.2.1 | 85 | 9/28/2024 |
4.2.1 | 86 | 9/28/2024 |
3.2.2 | 87 | 9/28/2024 |
3.1.1 | 102 | 9/28/2024 |
2.2.1 | 95 | 9/28/2024 |
2.1.0 | 89 | 9/28/2024 |
1.0.0 | 87 | 9/28/2024 |