Skip to content

Commit 95647d0

Browse files
committed
Add IDomainContextAccessor to pass IDomainContext in a nested domain service call
1 parent df350b0 commit 95647d0

File tree

9 files changed

+85
-23
lines changed

9 files changed

+85
-23
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using System.Threading;
5+
6+
namespace Wodsoft.ComBoost
7+
{
8+
public class DomainContextAccessor : IDomainContextAccessor
9+
{
10+
private readonly AsyncLocal<IDomainContext> _store = new AsyncLocal<IDomainContext>();
11+
private readonly IDomainContextProvider _provider;
12+
13+
public DomainContextAccessor(IDomainContextProvider provider)
14+
{
15+
_provider = provider;
16+
}
17+
18+
public IDomainContext Context { get => _store.Value ?? _provider.GetContext(); set => _store.Value = value; }
19+
}
20+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace Wodsoft.ComBoost
6+
{
7+
public interface IDomainContextAccessor
8+
{
9+
IDomainContext Context { get; set; }
10+
}
11+
}

src/Wodsoft.ComBoost.Grpc.Client/ComBoostGrpcServiceBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ public IComBoostGrpcServiceBuilder UseTemplate<T>(CallOptions callOptions = defa
5353
});
5454
Services.AddTransient<T>(sp =>
5555
{
56-
var contextProvider = sp.GetRequiredService<IDomainContextProvider>();
56+
var contextAccessor = sp.GetRequiredService<IDomainContextAccessor>();
5757
var templateDescriptor = sp.GetRequiredService<IDomainTemplateDescriptor<T>>();
58-
var context = contextProvider.GetContext();
58+
var context = contextAccessor.Context;
5959
return templateDescriptor.GetTemplate(context);
6060
});
6161
return this;

src/Wodsoft.ComBoost.Mock/DomainMockDependencyInjectionExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ public static class DomainMockDependencyInjectionExtensions
1717
public static IComBoostBuilder AddMock(this IComBoostBuilder builder, Action<IComBoostMockBuilder>? builderConfigure = null)
1818
{
1919
builder.Services.AddInMemorySemaphoreProvider();
20-
builder.Services.AddScoped<IDomainContextProvider, MockDomainContextProvider>();
20+
builder.Services.AddSingleton<IDomainContextAccessor, DomainContextAccessor>();
21+
builder.Services.PostConfigure<CompositeDomainContextProviderOptions>(options => options.TryAddContextProvider<MockDomainContextProvider>(0));
2122
builder.Services.AddScoped<MockAuthenticationSettings>();
2223
builder.Services.PostConfigure<AuthenticationProviderOptions>(options =>
2324
{

src/Wodsoft.ComBoost/ComBoostDependenceInjectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public static IComBoostBuilder AddComBoost(this IServiceCollection services)
2424
var manager = new DomainServiceEventManager(options.Value);
2525
return manager;
2626
});
27+
services.AddSingleton<IDomainContextAccessor, DomainContextAccessor>();
2728
services.TryAddScoped<IDomainContextProvider, CompositeDomainContextProvider>();
2829
services.TryAddScoped<IAuthenticationProvider, AuthenticationProvider>();
2930
services.AddScoped<IAuthorizationProvider, DefaultAuthorizationProvider>();

src/Wodsoft.ComBoost/ComBoostLocalServiceBuilder.cs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ namespace Wodsoft.ComBoost
1111
public class ComBoostLocalServiceBuilder<TService> : IComBoostLocalServiceBuilder<TService>
1212
where TService : class, IDomainService
1313
{
14-
private static MethodInfo _GetRequiredServiceMethodInfo = typeof(ServiceProviderServiceExtensions).GetMethod("GetRequiredService", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(IServiceProvider) }, null);
15-
private static MethodInfo _GetContextMethodInfo = typeof(IDomainContextProvider).GetMethod("GetContext");
14+
private static MethodInfo _UseTemplateMethod = typeof(ComBoostLocalServiceBuilder<TService>).GetMethod("UseTemplate")!;
1615

1716
public ComBoostLocalServiceBuilder(IServiceCollection services, IComBoostLocalBuilder builder)
1817
{
@@ -22,16 +21,7 @@ public ComBoostLocalServiceBuilder(IServiceCollection services, IComBoostLocalBu
2221
var type = typeof(TService);
2322
var descriptors = type.GetCustomAttributes<DomainTemplateImplementerAttribute>();
2423
foreach (var descriptor in descriptors)
25-
{
26-
var descriptorType = typeof(IDomainTemplateDescriptor<>).MakeGenericType(descriptor.TemplateType);
27-
services.AddSingleton(descriptorType,
28-
typeof(DomainTemplateBuilder<,>).MakeGenericType(typeof(TService), descriptor.TemplateType));
29-
var spParameter = Expression.Parameter(typeof(IServiceProvider));
30-
var createMethod = (Func<IServiceProvider, object>)Expression.Lambda(typeof(Func<,>).MakeGenericType(typeof(IServiceProvider), descriptor.TemplateType),
31-
Expression.Call(Expression.Call(_GetRequiredServiceMethodInfo.MakeGenericMethod(descriptorType), spParameter), descriptorType.GetMethod("GetTemplate"), Expression.Call(Expression.Call(_GetRequiredServiceMethodInfo.MakeGenericMethod(typeof(IDomainContextProvider)), spParameter), _GetContextMethodInfo)),
32-
spParameter).Compile();
33-
Services.AddTransient(descriptor.TemplateType, createMethod);
34-
}
24+
_UseTemplateMethod.MakeGenericMethod(descriptor.TemplateType).Invoke(this, null);
3525
services.AddTransient<TService>();
3626
}
3727

@@ -80,9 +70,9 @@ public IComBoostLocalServiceBuilder<TService> UseTemplate<T>()
8070
Services.AddSingleton<IDomainTemplateDescriptor<T>, DomainTemplateBuilder<TService, T>>();
8171
Services.AddTransient<T>(sp =>
8272
{
83-
var contextProvider = sp.GetRequiredService<IDomainContextProvider>();
73+
var contextAccessor = sp.GetRequiredService<IDomainContextAccessor>();
8474
var templateDescriptor = sp.GetRequiredService<IDomainTemplateDescriptor<T>>();
85-
var context = contextProvider.GetContext();
75+
var context = contextAccessor.Context;
8676
return templateDescriptor.GetTemplate(context);
8777
});
8878
return this;

src/Wodsoft.ComBoost/DomainTemplateBuilder.cs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public class DomainTemplateBuilder
2929
internal static readonly MethodInfo _GetLifetimeStrategy = typeof(DomainTemplateAgent).GetMethod("GetLifetimeStrategy", BindingFlags.NonPublic | BindingFlags.Instance)!;
3030
internal static readonly ConstructorInfo _TransientDomainContextConstructor = typeof(TransientDomainContext).GetConstructor(new Type[] { typeof(IDomainContext) })!;
3131
internal static readonly MethodInfo _GetTypeFromHandleMethod = typeof(Type).GetMethod("GetTypeFromHandle", new Type[1] { typeof(RuntimeTypeHandle) })!;
32+
internal static readonly MethodInfo _SetContext = typeof(IDomainContextAccessor).GetProperty("Context")!.GetSetMethod()!;
3233

3334
public static ModuleBuilder Module { get; }
3435

@@ -77,11 +78,11 @@ public class DomainTemplateBuilder<TDomainService, T> : DomainTemplateBuilder, I
7778
//internal static readonly MethodInfo _GetDomainService = typeof(DomainTemplateAgent<TDomainService>).GetProperty(nameof(DomainTemplateAgent<TDomainService>.Service))!.GetGetMethod()!;
7879
internal static readonly ConstructorInfo _AgentConstructor = typeof(DomainTemplateAgent).GetConstructor(new Type[] { typeof(IDomainContext) })!;
7980
internal static readonly MethodInfo _GetValueProvider = typeof(IDomainContext).GetProperty(nameof(IDomainContext.ValueProvider))!.GetGetMethod()!;
80-
private static Dictionary<string, FromAttribute[]> _FromAttributes;
81+
private static Dictionary<string, FromAttribute?[]> _FromAttributes;
8182
private static Dictionary<string, ParameterInfo[]> _ParameterInfos;
8283
private static TypeBuilder _AgentBuilder;
8384

84-
public static FromAttribute GetAttribute(string method, int parameterIndex)
85+
public static FromAttribute? GetAttribute(string method, int parameterIndex)
8586
{
8687
var values = _FromAttributes[method];
8788
return values[parameterIndex];
@@ -98,7 +99,7 @@ static DomainTemplateBuilder()
9899
var serviceType = typeof(TDomainService);
99100
var type = typeof(T);
100101
_FromAttributes = serviceType.GetMethods().ToDictionary(x => x.Name,
101-
x => x.GetParameters().Select(y => (FromAttribute)y.GetCustomAttributes().FirstOrDefault(z => z is FromAttribute)).ToArray());
102+
x => x.GetParameters().Select(y => (FromAttribute?)y.GetCustomAttributes().FirstOrDefault(z => z is FromAttribute)).ToArray());
102103
_ParameterInfos = serviceType.GetMethods().ToDictionary(x => x.Name, x => x.GetParameters());
103104

104105
if (!type.IsInterface)
@@ -176,13 +177,15 @@ static DomainTemplateBuilder()
176177
//Set each parameter value into values. values.Add(parameter name, parameter value)
177178
for (int i = 0; i < parameters.Length; i++)
178179
{
180+
if (parameters[i].Name == null)
181+
continue;
179182
ilGenerator.Emit(OpCodes.Ldloc, values);
180-
ilGenerator.Emit(OpCodes.Ldstr, parameters[i].Name);
183+
ilGenerator.Emit(OpCodes.Ldstr, parameters[i].Name!);
181184
ilGenerator.Emit(OpCodes.Ldarg_S, (byte)(i + 1));
182185
if (parameters[i].ParameterType.IsValueType)
183186
ilGenerator.Emit(OpCodes.Box, parameters[i].ParameterType);
184187
ilGenerator.Emit(OpCodes.Call, _DictionaryAdd);
185-
parameterNames.Add(parameters[i].Name);
188+
parameterNames.Add(parameters[i].Name!);
186189
}
187190

188191
var valueAttributes = method.GetCustomAttributes<DomainValueAttribute>();
@@ -262,11 +265,19 @@ static DomainTemplateBuilder()
262265
ilGenerator.Emit(OpCodes.Brfalse_S, scopeLabel);
263266
ilGenerator.Emit(OpCodes.Newobj, _TransientDomainContextConstructor);
264267
ilGenerator.MarkLabel(scopeLabel);
268+
ilGenerator.Emit(OpCodes.Dup);
265269
ilGenerator.Emit(OpCodes.Stloc, domainContextLocal);
266270

271+
//domainContext.GetService<IDomainContextAccessor>().Context = domainContext;
272+
ilGenerator.Emit(OpCodes.Ldtoken, typeof(IDomainContextAccessor));
273+
ilGenerator.Emit(OpCodes.Call, _GetTypeFromHandleMethod);
274+
ilGenerator.Emit(OpCodes.Callvirt, _GetService);
275+
ilGenerator.Emit(OpCodes.Ldloc, domainContextLocal);
276+
ilGenerator.Emit(OpCodes.Callvirt, _SetContext);
277+
267278
var valueProviderLocal = ilGenerator.DeclareLocal(typeof(IValueProvider));
268279

269-
//var valueProvider = (IValueProvider)domainContext.GetService(typeof(IValueProvider));
280+
//var valueProvider = domainContext.ValueProvider;
270281
ilGenerator.Emit(OpCodes.Ldloc, domainContextLocal);
271282
ilGenerator.Emit(OpCodes.Callvirt, _GetValueProvider);
272283
ilGenerator.Emit(OpCodes.Stloc, valueProviderLocal);

test/Wodsoft.ComBoost.Test.Common/LifetimeService.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
34
using System.Text;
45
using System.Threading.Tasks;
56

@@ -18,5 +19,12 @@ public Task<int> TransientIncrease([FromService] LifetimeCounter counter)
1819
{
1920
return Task.FromResult(++counter.Count);
2021
}
22+
23+
[DomainLifetimeStrategy(DomainLifetimeStrategy.Transient)]
24+
public Task DomainContextAccessor([FromService] ILifetimeService lifetimeService)
25+
{
26+
Debug.Assert(lifetimeService.Context is TransientDomainContext);
27+
return Task.CompletedTask;
28+
}
2129
}
2230
}

test/Wodsoft.ComBoost.Test/ServiceTest.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,5 +154,25 @@ public async Task LifetimeTest()
154154
Assert.Equal(1, count);
155155
}
156156
}
157+
158+
[Fact]
159+
public async Task DomainContextAccessorTest()
160+
{
161+
ServiceCollection services = new ServiceCollection();
162+
services.AddScoped<LifetimeCounter>();
163+
services.AddComBoost()
164+
.AddLocalService(builder =>
165+
{
166+
builder.AddService<LifetimeService>();
167+
})
168+
.AddMock();
169+
var serviceProvider = services.BuildServiceProvider();
170+
171+
using (var scope = serviceProvider.CreateScope())
172+
{
173+
var service = scope.ServiceProvider.GetRequiredService<ILifetimeService>();
174+
await service.DomainContextAccessor();
175+
}
176+
}
157177
}
158178
}

0 commit comments

Comments
 (0)