Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions Autoredi.slnx
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
<Solution>
<Configurations>
<Configuration Name="Debug" />
<Configuration Name="Release" />
</Configurations>

<Folder Name="/src/">
<Project Path="src/Autoredi/Autoredi.csproj" />
<Project Path="src/Autoredi.Generators/Autoredi.Generators.csproj" />
Expand All @@ -19,6 +14,8 @@
<Project Path="samples/Samples.SingleInterface/Samples.SingleInterface.csproj" />
<Project Path="samples/Samples.KeyedServices/Samples.KeyedServices.csproj" />
<Project Path="samples/Samples.Complete/Samples.Complete.csproj" />
<Project Path="samples/Samples.Modular.Infrastructure/Samples.Modular.Infrastructure.csproj" />
<Project Path="samples/Samples.Modular.App/Samples.Modular.App.csproj" />
</Folder>

<Folder Name="/benchmarks/">
Expand Down
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Changelog

All notable changes to this project will be documented in this file.

## [0.2.0] - 2026-01-28

### Added
- **Group Property**: Organize services into logical groups with selective registration.
- `AddAutorediServices()` still registers all services (backward compatible).
- New group-specific methods: `AddAutorediServicesFirebase()`, `AddAutorediServicesAccount()`, etc.
- Services without a group go to `AddAutorediServicesDefault()`.
- Global aggregation: Group methods automatically include services from the same group in referenced assemblies.
- **Priority Property**: Control registration order within groups using `priority` (int).
- Higher values are registered first (e.g., 100 before 0).
- Default priority is 0.
- Useful for controlling service registration order for decorators or overrides.
- **Modular Sample**: Added `Samples.Modular` demonstrating cross-assembly grouping and priority.
- **Performance Benchmarks**: Added `GroupingBenchmarks` to measure selective registration overhead.
- Selective registration is faster than full registration for large containers.
- Priority sorting adds zero runtime overhead (pre-sorted in generated code).

### Changed
- `AutorediAttribute` now accepts optional `group` and `priority` parameters.
- Generated extension methods now include group-specific registration methods.
- Version bumped to 0.2.0.

### Backward Compatibility
- ✅ All existing code continues to work without changes.
- ✅ New parameters are optional with sensible defaults.
- ✅ `AddAutorediServices()` behavior remains unchanged (registers all services).
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Autoredi is a powerful source generator for .NET that simplifies dependency inje
- [Intermediate: Single Interface Implementation](#intermediate-single-interface-implementation)
- [Advanced: Keyed Services for Multiple Implementations](#advanced-keyed-services-for-multiple-implementations)
- [Complex: Controllers and Dynamic Resolution](#complex-controllers-and-dynamic-resolution)
- [Grouped Registration](#grouped-registration)
- [Priority Ordering](#priority-ordering)
- [Contributing](#contributing)
- [License](#license)

Expand Down Expand Up @@ -290,6 +292,63 @@ In this scenario:

This demonstrates Autoredi’s flexibility in handling complex DI scenarios, from constructor injection to runtime service selection.

### Grouped Registration

For large applications, you can organize services into named groups for selective registration. This is useful for modularizing your DI setup or conditionally registering sets of services.

```csharp
// Group: "Firebase"
[Autoredi(ServiceLifetime.Singleton, group: "Firebase")]
public class FirebaseConfig { }

[Autoredi(ServiceLifetime.Transient, group: "Firebase")]
public class FirebaseRepository { }

// Group: "Account"
[Autoredi(ServiceLifetime.Scoped, group: "Account")]
public class AccountService { }

// No Group (Default)
[Autoredi(ServiceLifetime.Transient)]
public class GlobalService { }
```

**Usage:**

```csharp
var services = new ServiceCollection();

// Option 1: Register EVERYTHING (Default behavior)
services.AddAutorediServices();

// Option 2: Selective Registration
services.AddAutorediServicesFirebase(); // Registers only Firebase group
services.AddAutorediServicesAccount(); // Registers only Account group
services.AddAutorediServicesDefault(); // Registers ungrouped services
```

*Note: Group registration methods (e.g., `AddAutorediServicesFirebase`) automatically include services from the same group in referenced assemblies. Check the `samples/Samples.Modular` directory for a complete cross-project example.*

### Priority Ordering

You can control the order in which services are registered within their groups using the `priority` parameter. Higher values are registered first.

```csharp
// Priority 100: Registered first
[Autoredi(ServiceLifetime.Singleton, priority: 100)]
public class FirstService { }

// Priority 50: Registered second
[Autoredi(ServiceLifetime.Singleton, priority: 50)]
public class SecondService { }

// Default Priority (0): Registered last (in alphabetical order)
[Autoredi(ServiceLifetime.Singleton)]
public class LastService { }
```

Priorities are scoped to their group (or the default group). This is helpful when service registration order matters, such as when using decorators or the `TryAdd` pattern (though Autoredi always uses `Add`).

## Contributing

Contributions are welcome! To get started:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
using BenchmarkDotNet.Attributes;
using Microsoft.Extensions.DependencyInjection;
using Autoredi.Benchmarks.Autoredi;
using Autoredi.Benchmarks.Services.Generated;

namespace Autoredi.Benchmarks.Benchmarks;

[MemoryDiagnoser]
[CategoriesColumn]
[GroupBenchmarksBy(BenchmarkDotNet.Configs.BenchmarkLogicalGroupRule.ByCategory)]
public class ComprehensiveRegistrationBenchmarks
{
// ========================================================================
// 1. Full Registration (All Services)
// ========================================================================

[Benchmark(Description = "Autoredi - All Groups (40 Services)", Baseline = false)]
[BenchmarkCategory("Full_Registration")]
public void Autoredi_All()
{
var services = new ServiceCollection();
services.AddAutorediServices();
}

[Benchmark(Description = "Manual - All Groups (40 Services)", Baseline = true)]
[BenchmarkCategory("Full_Registration")]
public void Manual_All()
{
var services = new ServiceCollection();

// Singletons
services.AddSingleton<Singleton_0>();
services.AddSingleton<Singleton_1>();
services.AddSingleton<Singleton_2>();
services.AddSingleton<Singleton_3>();
services.AddSingleton<Singleton_4>();
services.AddSingleton<Singleton_5>();
services.AddSingleton<Singleton_6>();
services.AddSingleton<Singleton_7>();
services.AddSingleton<Singleton_8>();
services.AddSingleton<Singleton_9>();

// Scopeds
services.AddScoped<Scoped_0>();
services.AddScoped<Scoped_1>();
services.AddScoped<Scoped_2>();
services.AddScoped<Scoped_3>();
services.AddScoped<Scoped_4>();
services.AddScoped<Scoped_5>();
services.AddScoped<Scoped_6>();
services.AddScoped<Scoped_7>();
services.AddScoped<Scoped_8>();
services.AddScoped<Scoped_9>();

// Transients
services.AddTransient<Transient_0>();
services.AddTransient<Transient_1>();
services.AddTransient<Transient_2>();
services.AddTransient<Transient_3>();
services.AddTransient<Transient_4>();
services.AddTransient<Transient_5>();
services.AddTransient<Transient_6>();
services.AddTransient<Transient_7>();
services.AddTransient<Transient_8>();
services.AddTransient<Transient_9>();

// Priority
services.AddSingleton<PriorityHigh_0>();
services.AddScoped<PriorityHigh_1>();
services.AddTransient<PriorityHigh_2>();
services.AddSingleton<PriorityMid_0>();
services.AddScoped<PriorityMid_1>();
services.AddTransient<PriorityMid_2>();
services.AddSingleton<PriorityLow_0>();
services.AddScoped<PriorityLow_1>();
services.AddTransient<PriorityLow_2>();
services.AddSingleton<PriorityMin_0>();
}

// ========================================================================
// 2. Lifetime Specific Groups
// ========================================================================

[Benchmark(Description = "Autoredi - Singletons (10)", Baseline = false)]
[BenchmarkCategory("Lifetime_Singleton")]
public void Autoredi_Singletons()
{
var services = new ServiceCollection();
services.AddAutorediServicesSingletons();
}

[Benchmark(Description = "Manual - Singletons (10)", Baseline = true)]
[BenchmarkCategory("Lifetime_Singleton")]
public void Manual_Singletons()
{
var services = new ServiceCollection();
services.AddSingleton<Singleton_0>();
services.AddSingleton<Singleton_1>();
services.AddSingleton<Singleton_2>();
services.AddSingleton<Singleton_3>();
services.AddSingleton<Singleton_4>();
services.AddSingleton<Singleton_5>();
services.AddSingleton<Singleton_6>();
services.AddSingleton<Singleton_7>();
services.AddSingleton<Singleton_8>();
services.AddSingleton<Singleton_9>();
}

[Benchmark(Description = "Autoredi - Transients (10)", Baseline = false)]
[BenchmarkCategory("Lifetime_Transient")]
public void Autoredi_Transients()
{
var services = new ServiceCollection();
services.AddAutorediServicesTransients();
}

[Benchmark(Description = "Manual - Transients (10)", Baseline = true)]
[BenchmarkCategory("Lifetime_Transient")]
public void Manual_Transients()
{
var services = new ServiceCollection();
services.AddTransient<Transient_0>();
services.AddTransient<Transient_1>();
services.AddTransient<Transient_2>();
services.AddTransient<Transient_3>();
services.AddTransient<Transient_4>();
services.AddTransient<Transient_5>();
services.AddTransient<Transient_6>();
services.AddTransient<Transient_7>();
services.AddTransient<Transient_8>();
services.AddTransient<Transient_9>();
}

// ========================================================================
// 3. Priority & Mixing
// ========================================================================

[Benchmark(Description = "Autoredi - Prioritized Group (10 Mixed)", Baseline = false)]
[BenchmarkCategory("Priority_Mixed")]
public void Autoredi_Prioritized()
{
var services = new ServiceCollection();
services.AddAutorediServicesPrioritized();
}

[Benchmark(Description = "Manual - Prioritized Group (10 Mixed)", Baseline = true)]
[BenchmarkCategory("Priority_Mixed")]
public void Manual_Prioritized()
{
var services = new ServiceCollection();
// Manually registering in the correct priority order to match Autoredi's output
// High (100)
services.AddSingleton<PriorityHigh_0>();
services.AddScoped<PriorityHigh_1>();
services.AddTransient<PriorityHigh_2>();
// Mid (50)
services.AddSingleton<PriorityMid_0>();
services.AddScoped<PriorityMid_1>();
services.AddTransient<PriorityMid_2>();
// Low (0)
services.AddSingleton<PriorityLow_0>();
services.AddScoped<PriorityLow_1>();
services.AddTransient<PriorityLow_2>();
// Min (0)
services.AddSingleton<PriorityMin_0>();
}
}
34 changes: 34 additions & 0 deletions benchmarks/Autoredi.Benchmarks/Benchmarks/GroupingBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using BenchmarkDotNet.Attributes;
using Microsoft.Extensions.DependencyInjection;
using Autoredi.Benchmarks.Autoredi;
using Autoredi.Benchmarks.Services;

namespace Autoredi.Benchmarks.Benchmarks;

/// <summary>
/// Benchmarks comparing grouping and selective registration performance.
/// </summary>
[MemoryDiagnoser]
public class GroupingBenchmarks
{
[Benchmark(Description = "Autoredi - Full Registration (All Groups)")]
public void FullRegistration()
{
var services = new ServiceCollection();
services.AddAutorediServices();
}

[Benchmark(Description = "Autoredi - Selective Registration (GroupA Only)")]
public void SelectiveRegistration()
{
var services = new ServiceCollection();
services.AddAutorediServicesGroupA();
}

[Benchmark(Description = "Autoredi - Default Group Only")]
public void DefaultGroupOnly()
{
var services = new ServiceCollection();
services.AddAutorediServicesDefault();
}
}
18 changes: 16 additions & 2 deletions benchmarks/Autoredi.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,21 @@ static void Main(string[] args)
Console.WriteLine("Comparing Autoredi vs Manual DI Registration Performance\n");

Console.WriteLine("Available Benchmarks:");
Console.WriteLine(" 1. ContainerBuildBenchmarks - Container build performance");
Console.WriteLine(" 1. ContainerBuildBenchmarks - Basic build performance");
Console.WriteLine(" 2. ServiceResolutionBenchmarks - Service resolution performance");
Console.WriteLine(" 3. ScopedResolutionBenchmarks - Scoped service resolution");
Console.WriteLine(" 4. All - Run all benchmarks\n");
Console.WriteLine(" 4. GroupingBenchmarks - Basic grouping performance");
Console.WriteLine(" 5. ComprehensiveRegistrationBenchmarks - Detailed Manual vs Autoredi (Groups, Priorities, Lifetimes)");
Console.WriteLine(" 6. All - Run all benchmarks\n");

if (args.Length == 0)
{
Console.WriteLine("Running all benchmarks...\n");
BenchmarkRunner.Run<ContainerBuildBenchmarks>();
BenchmarkRunner.Run<ServiceResolutionBenchmarks>();
BenchmarkRunner.Run<ScopedResolutionBenchmarks>();
BenchmarkRunner.Run<GroupingBenchmarks>();
BenchmarkRunner.Run<ComprehensiveRegistrationBenchmarks>();
}
else
{
Expand All @@ -43,11 +47,21 @@ static void Main(string[] args)
BenchmarkRunner.Run<ScopedResolutionBenchmarks>();
break;
case "4":
case "grouping":
BenchmarkRunner.Run<GroupingBenchmarks>();
break;
case "5":
case "comprehensive":
BenchmarkRunner.Run<ComprehensiveRegistrationBenchmarks>();
break;
case "6":
case "all":
default:
BenchmarkRunner.Run<ContainerBuildBenchmarks>();
BenchmarkRunner.Run<ServiceResolutionBenchmarks>();
BenchmarkRunner.Run<ScopedResolutionBenchmarks>();
BenchmarkRunner.Run<GroupingBenchmarks>();
BenchmarkRunner.Run<ComprehensiveRegistrationBenchmarks>();
break;
}
}
Expand Down
Loading
Loading