Skip to content

Commit 5b56e0b

Browse files
committed
Feat: Add AddServiceIfNotExists extension for IServiceCollection
Introduces the AddServiceIfNotExists method to register services only if they are not already registered. Includes support for specifying lifetimes and prevents duplicate registrations. Comprehensive tests are added to validate behavior for various lifetimes, ensuring robustness and error handling.
1 parent 5296534 commit 5b56e0b

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// ---------------------------------------------------------------------------------------------------------------------
2+
// Imports
3+
// ---------------------------------------------------------------------------------------------------------------------
4+
// ReSharper disable once CheckNamespace
5+
namespace Microsoft.Extensions.DependencyInjection;
6+
// ---------------------------------------------------------------------------------------------------------------------
7+
// Code
8+
// ---------------------------------------------------------------------------------------------------------------------
9+
public static class IServiceCollectionExtensions {
10+
public static IServiceCollection AddServiceIfNotExists<TService, TImplementation>(
11+
this IServiceCollection services,
12+
ServiceLifetime lifetime = ServiceLifetime.Scoped)
13+
where TService : class
14+
where TImplementation : class, TService
15+
{
16+
if (services.FirstOrDefault(x => x.ServiceType == typeof(TService)) is not null) return services;
17+
18+
switch (lifetime) {
19+
case ServiceLifetime.Singleton: services.AddSingleton<TService, TImplementation>();
20+
break;
21+
case ServiceLifetime.Scoped: services.AddScoped<TService, TImplementation>();
22+
break;
23+
case ServiceLifetime.Transient: services.AddTransient<TService, TImplementation>();
24+
break;
25+
default: throw new ArgumentOutOfRangeException(nameof(lifetime), lifetime, null);
26+
}
27+
28+
return services;
29+
}
30+
31+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// ---------------------------------------------------------------------------------------------------------------------
2+
// Imports
3+
// ---------------------------------------------------------------------------------------------------------------------
4+
using Microsoft.Extensions.DependencyInjection;
5+
6+
namespace Tests.CodeOfChaos.Extensions.DependencyInjection;
7+
// ---------------------------------------------------------------------------------------------------------------------
8+
// Code
9+
// ---------------------------------------------------------------------------------------------------------------------
10+
public class AddServiceIfNotExistsTests {
11+
[Test]
12+
public async Task AddServiceIfNotExists_WhenServiceDoesNotExist_AddsWithDefaultScoped() {
13+
// Arrange
14+
var services = new ServiceCollection();
15+
16+
// Act
17+
services.AddServiceIfNotExists<ITestService, TestService>();
18+
ServiceDescriptor? descriptor = services.FirstOrDefault(x => x.ServiceType == typeof(ITestService));
19+
20+
// Assert
21+
await Assert.That(descriptor).IsNotNull();
22+
await Assert.That(descriptor!.ImplementationType).IsEqualTo(typeof(TestService));
23+
await Assert.That(descriptor.Lifetime).IsEqualTo(ServiceLifetime.Scoped);
24+
}
25+
26+
[Test]
27+
[Arguments(ServiceLifetime.Singleton)]
28+
[Arguments(ServiceLifetime.Scoped)]
29+
[Arguments(ServiceLifetime.Transient)]
30+
public async Task AddServiceIfNotExists_WithSpecificLifetime_AddsServiceCorrectly(ServiceLifetime lifetime) {
31+
// Arrange
32+
var services = new ServiceCollection();
33+
34+
// Act
35+
services.AddServiceIfNotExists<ITestService, TestService>(lifetime);
36+
ServiceDescriptor? descriptor = services.FirstOrDefault(x => x.ServiceType == typeof(ITestService));
37+
38+
// Assert
39+
await Assert.That(descriptor).IsNotNull();
40+
await Assert.That(descriptor!.Lifetime).IsEqualTo(lifetime);
41+
}
42+
43+
[Test]
44+
public async Task AddServiceIfNotExists_WhenServiceExists_DoesNotAddDuplicate() {
45+
// Arrange
46+
var services = new ServiceCollection();
47+
services.AddScoped<ITestService, TestService>();
48+
int initialCount = services.Count;
49+
50+
// Act
51+
services.AddServiceIfNotExists<ITestService, TestService>();
52+
53+
// Assert
54+
await Assert.That(services.Count).IsEqualTo(initialCount);
55+
}
56+
57+
[Test]
58+
public async Task AddServiceIfNotExists_WithInvalidLifetime_ThrowsException() {
59+
// Arrange
60+
var services = new ServiceCollection();
61+
62+
// Act & Assert
63+
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => {
64+
services.AddServiceIfNotExists<ITestService, TestService>((ServiceLifetime)999);
65+
return Task.CompletedTask;
66+
});
67+
}
68+
69+
// Test interfaces and classes
70+
private interface ITestService { }
71+
private class TestService : ITestService { }
72+
}

0 commit comments

Comments
 (0)