Skip to content

Commit 338378a

Browse files
committed
more extensions
1 parent 0da453c commit 338378a

File tree

5 files changed

+1056
-87
lines changed

5 files changed

+1056
-87
lines changed

Eocron.DependencyInjection.Tests/DecoratorTests.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,22 @@ public void Decoration()
1717
{
1818
var mock = new Mock<ITest>();
1919
var sc = new ServiceCollection();
20-
Func<IServiceProvider, object> factory = (_) => mock.Object;
21-
sc.Add(new ServiceDescriptor(typeof(ITest),factory, ServiceLifetime.Transient),
22-
new DecoratorChain()
20+
sc.AddTransient<ITest>(_=> mock.Object,
21+
c => c
2322
.Add((sp, o) => o)
2423
.Add((sp, o) => o));
2524

2625
sc.Select(x => x.ServiceKey).Should().Equal(["000001_decorator", "000002_decorator", null]);
27-
sc.Should().ContainSingle(x=> x.ServiceType == typeof(ITest) && x.Lifetime == ServiceLifetime.Transient && !x.IsKeyedService && x.ImplementationFactory != factory);
26+
sc.Should().ContainSingle(x=> x.ServiceType == typeof(ITest) && x.Lifetime == ServiceLifetime.Transient && !x.IsKeyedService);
2827
}
2928

3029
[Test]
3130
public void NoDecoration()
3231
{
3332
var mock = new Mock<ITest>();
3433
var sc = new ServiceCollection();
35-
Func<IServiceProvider, object> factory = (_) => mock.Object;
36-
sc.Add(new ServiceDescriptor(typeof(ITest),factory, ServiceLifetime.Transient),
37-
new DecoratorChain());
34+
sc.AddTransient<ITest>(_=> mock.Object,
35+
c => {});
3836

3937
sc.Should().ContainSingle(x=> x.ServiceType == typeof(ITest) && x.Lifetime == ServiceLifetime.Transient && !x.IsKeyedService);
4038
sc.Select(x => x.ServiceKey).Should().Equal([null]);
@@ -50,9 +48,8 @@ public void DecorationExecutionOrder()
5048
sb.Append("implementation_call_"+x);
5149
});
5250
var sc = new ServiceCollection();
53-
Func<IServiceProvider, object> factory = (_) => mock.Object;
54-
sc.Add(new ServiceDescriptor(typeof(ITest),factory, ServiceLifetime.Transient),
55-
new DecoratorChain()
51+
sc.AddTransient<ITest>(_=> mock.Object,
52+
c => c
5653
.Add((sp, o) =>
5754
{
5855
sb.Append("second_call ");

Eocron.DependencyInjection/DecoratorChain.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace Eocron.DependencyInjection
44
{
55
public sealed class DecoratorChain
66
{
7-
private readonly List<DecoratorDelegate> _items = new List<DecoratorDelegate>();
7+
private readonly List<DecoratorDelegate> _items = new();
88
public IReadOnlyList<DecoratorDelegate> Items => _items;
99

1010
public DecoratorChain Add(DecoratorDelegate decorator)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using System;
2+
using System.Threading;
3+
using Microsoft.Extensions.DependencyInjection;
4+
5+
namespace Eocron.DependencyInjection
6+
{
7+
public static partial class ServiceCollectionServiceExtensions
8+
{
9+
public static IServiceCollection Add(this IServiceCollection services, ServiceDescriptor descriptor, DecoratorChain chain)
10+
{
11+
ArgumentNullException.ThrowIfNull(descriptor);
12+
ArgumentNullException.ThrowIfNull(chain);
13+
14+
if (chain.Items.Count == 0)
15+
{
16+
services.Add(descriptor);
17+
return services;
18+
}
19+
20+
var implementationDescriptor = CloneWithNewKey(descriptor);
21+
services.Add(implementationDescriptor);
22+
var prevKey = implementationDescriptor.ServiceKey;
23+
for (var i = chain.Items.Count - 1; i >= 0; i--)
24+
{
25+
var d = chain.Items[i];
26+
var isLast = i == 0;
27+
var pk = prevKey;
28+
if (isLast)
29+
{
30+
if (descriptor.IsKeyedService)
31+
{
32+
services.Add(new ServiceDescriptor(
33+
descriptor.ServiceType,
34+
descriptor.ServiceKey,
35+
(sp, _) => d(sp, sp.GetRequiredKeyedService(descriptor.ServiceType, pk)),
36+
descriptor.Lifetime));
37+
}
38+
else
39+
{
40+
services.Add(new ServiceDescriptor(
41+
descriptor.ServiceType,
42+
sp => d(sp, sp.GetRequiredKeyedService(descriptor.ServiceType, pk)),
43+
descriptor.Lifetime));
44+
}
45+
}
46+
else
47+
{
48+
var nextKey = GenerateKey();
49+
services.Add(new ServiceDescriptor(
50+
descriptor.ServiceType,
51+
nextKey,
52+
(sp, _) => d(sp, sp.GetRequiredKeyedService(descriptor.ServiceType, pk)),
53+
descriptor.Lifetime));
54+
prevKey = nextKey;
55+
}
56+
}
57+
58+
return services;
59+
}
60+
61+
private static string GenerateKey()
62+
{
63+
return Interlocked.Increment(ref _counter).ToString("000000") + "_decorator";
64+
}
65+
66+
private static long _counter = 0;
67+
68+
private static ServiceDescriptor CloneWithNewKey(ServiceDescriptor descriptor)
69+
{
70+
var newKey = GenerateKey();
71+
if (descriptor.IsKeyedService)
72+
{
73+
if (descriptor.KeyedImplementationInstance != null)
74+
{
75+
return new ServiceDescriptor(descriptor.ServiceType, newKey, (_,_)=> descriptor.KeyedImplementationInstance, descriptor.Lifetime);
76+
}
77+
if (descriptor.KeyedImplementationFactory != null)
78+
{
79+
return new ServiceDescriptor(descriptor.ServiceType, newKey, (sp,_)=> descriptor.KeyedImplementationFactory(sp, descriptor.ServiceKey), descriptor.Lifetime);
80+
}
81+
return new ServiceDescriptor(descriptor.ServiceType, newKey, descriptor.KeyedImplementationType, descriptor.Lifetime);
82+
}
83+
84+
if (descriptor.ImplementationInstance != null)
85+
{
86+
return new ServiceDescriptor(descriptor.ServiceType, newKey, (_,_)=> descriptor.ImplementationInstance, descriptor.Lifetime);
87+
}
88+
if (descriptor.ImplementationFactory != null)
89+
{
90+
return new ServiceDescriptor(descriptor.ServiceType, newKey, (sp,_)=> descriptor.ImplementationFactory(sp), descriptor.Lifetime);
91+
}
92+
return new ServiceDescriptor(descriptor.ServiceType, newKey, descriptor.ImplementationType, descriptor.Lifetime);
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)