1
1
#if NET8_0_OR_GREATER
2
2
using Microsoft . Extensions . DependencyInjection ;
3
+ using Microsoft . Extensions . DependencyInjection . Specification . Fakes ;
3
4
using System ;
4
5
using System . Linq ;
6
+ using System . Threading ;
7
+ using System . Threading . Tasks ;
5
8
using Xunit ;
6
9
7
10
namespace Castle . Windsor . Extensions . DependencyInjection . Tests
@@ -36,28 +39,232 @@ public void Resolve_All()
36
39
Assert . IsType < AnotherTestService > ( keyedServices . Last ( ) ) ;
37
40
}
38
41
42
+ [ Fact ]
43
+ public void Scoped_keyed_service_resolved_by_thread_outside_scope ( )
44
+ {
45
+ Boolean stop = false ;
46
+ Boolean shouldResolve = false ;
47
+ ITestService resolvedInThread = null ;
48
+ var thread = new Thread ( _ =>
49
+ {
50
+ while ( ! stop )
51
+ {
52
+ Thread . Sleep ( 100 ) ;
53
+ if ( shouldResolve )
54
+ {
55
+ stop = true ;
56
+ resolvedInThread = _serviceProvider . GetRequiredKeyedService < ITestService > ( "porcodio" ) ;
57
+ }
58
+ }
59
+ } ) ;
60
+ thread . Start ( ) ;
61
+
62
+ var serviceCollection = GetServiceCollection ( ) ;
63
+ serviceCollection . AddKeyedScoped < ITestService , TestService > ( "porcodio" ) ;
64
+ _serviceProvider = BuildServiceProvider ( serviceCollection ) ;
65
+
66
+ //resolved outside scope
67
+ ITestService resolvedOutsideScope = _serviceProvider . GetRequiredKeyedService < ITestService > ( "porcodio" ) ;
68
+
69
+ // resolve in scope
70
+ ITestService resolvedInScope ;
71
+ using ( var scope = _serviceProvider . CreateScope ( ) )
72
+ {
73
+ resolvedInScope = scope . ServiceProvider . GetRequiredKeyedService < ITestService > ( "porcodio" ) ;
74
+ }
75
+
76
+ shouldResolve = true ;
77
+ //now wait for the original thread to finish
78
+ thread . Join ( 1000 * 10 ) ;
79
+ Assert . NotNull ( resolvedInThread ) ;
80
+ Assert . NotNull ( resolvedOutsideScope ) ;
81
+ Assert . NotNull ( resolvedInScope ) ;
82
+
83
+ Assert . NotEqual ( resolvedInScope , resolvedOutsideScope ) ;
84
+ Assert . NotEqual ( resolvedInScope , resolvedInThread ) ;
85
+ Assert . Equal ( resolvedOutsideScope , resolvedInThread ) ;
86
+ }
87
+
88
+ [ Fact ]
89
+ public void Scoped_service_resolved_outside_scope ( )
90
+ {
91
+ var serviceCollection = GetServiceCollection ( ) ;
92
+ serviceCollection . AddScoped < ITestService , TestService > ( ) ;
93
+ _serviceProvider = BuildServiceProvider ( serviceCollection ) ;
94
+
95
+ //resolved outside scope
96
+ ITestService resolvedOutsideScope = _serviceProvider . GetRequiredService < ITestService > ( ) ;
97
+ Assert . NotNull ( resolvedOutsideScope ) ;
98
+
99
+ // resolve in scope
100
+ ITestService resolvedInScope ;
101
+ using ( var scope = _serviceProvider . CreateScope ( ) )
102
+ {
103
+ resolvedInScope = scope . ServiceProvider . GetRequiredService < ITestService > ( ) ;
104
+ }
105
+ Assert . NotNull ( resolvedInScope ) ;
106
+ Assert . NotEqual ( resolvedInScope , resolvedOutsideScope ) ;
107
+
108
+ ITestService resolvedAgainOutsideScope = _serviceProvider . GetRequiredService < ITestService > ( ) ;
109
+ Assert . NotNull ( resolvedAgainOutsideScope ) ;
110
+ Assert . Equal ( resolvedOutsideScope , resolvedAgainOutsideScope ) ;
111
+ }
112
+
113
+ [ Fact ]
114
+ public void Scoped_service_resolved_outside_scope_in_another_thread ( )
115
+ {
116
+ var serviceCollection = GetServiceCollection ( ) ;
117
+ serviceCollection . AddScoped < ITestService , TestService > ( ) ;
118
+ _serviceProvider = BuildServiceProvider ( serviceCollection ) ;
119
+
120
+ var task = Task . Run ( ( ) =>
121
+ {
122
+ //resolved outside scope
123
+ ITestService resolvedOutsideScope = _serviceProvider . GetRequiredService < ITestService > ( ) ;
124
+ Assert . NotNull ( resolvedOutsideScope ) ;
125
+
126
+ // resolve in scope
127
+ ITestService resolvedInScope ;
128
+ using ( var scope = _serviceProvider . CreateScope ( ) )
129
+ {
130
+ resolvedInScope = scope . ServiceProvider . GetRequiredService < ITestService > ( ) ;
131
+ }
132
+ Assert . NotNull ( resolvedInScope ) ;
133
+ Assert . NotEqual ( resolvedInScope , resolvedOutsideScope ) ;
134
+
135
+ ITestService resolvedAgainOutsideScope = _serviceProvider . GetRequiredService < ITestService > ( ) ;
136
+ Assert . NotNull ( resolvedAgainOutsideScope ) ;
137
+ Assert . Equal ( resolvedOutsideScope , resolvedAgainOutsideScope ) ;
138
+ return true ;
139
+ } ) ;
140
+
141
+ Assert . True ( task . Result ) ;
142
+ }
143
+
144
+ [ Fact ]
145
+ public async void Scoped_service_resolved_outside_scope_in_another_unsafe_thread ( )
146
+ {
147
+ var serviceCollection = GetServiceCollection ( ) ;
148
+ serviceCollection . AddScoped < ITestService , TestService > ( ) ;
149
+ _serviceProvider = BuildServiceProvider ( serviceCollection ) ;
150
+
151
+ var tsc = new TaskCompletionSource ( ) ;
152
+ var worker = new QueueUserWorkItemWorker ( _serviceProvider , tsc ) ;
153
+ ThreadPool . UnsafeQueueUserWorkItem ( worker , false ) ;
154
+ await tsc . Task ;
155
+
156
+ Assert . Null ( worker . ExecuteException ) ;
157
+ Assert . NotNull ( worker . ResolvedOutsideScope ) ;
158
+ Assert . NotNull ( worker . ResolvedInScope ) ;
159
+ Assert . NotEqual ( worker . ResolvedInScope , worker . ResolvedOutsideScope ) ;
160
+ }
161
+
162
+ [ Fact ]
163
+ public async void Simulate_async_timer_without_wait ( )
164
+ {
165
+ Boolean stop = false ;
166
+ Boolean shouldResolve = false ;
167
+ ITestService resolvedInThread = null ;
168
+ async Task ExecuteAsync ( )
169
+ {
170
+ while ( ! stop )
171
+ {
172
+ await Task . Delay ( 100 ) ;
173
+ if ( shouldResolve )
174
+ {
175
+ stop = true ;
176
+ resolvedInThread = _serviceProvider . GetService < ITestService > ( ) ;
177
+ }
178
+ }
179
+ }
180
+ //fire and forget
181
+ #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
182
+ var task = ExecuteAsync ( ) ;
183
+ #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
184
+
185
+ await Task . Delay ( 500 ) ;
186
+
187
+ var serviceCollection = GetServiceCollection ( ) ;
188
+ serviceCollection . AddScoped < ITestService , TestService > ( ) ;
189
+ _serviceProvider = BuildServiceProvider ( serviceCollection ) ;
190
+
191
+ //resolved outside scope
192
+ ITestService resolvedOutsideScope = _serviceProvider . GetRequiredService < ITestService > ( ) ;
193
+
194
+ // resolve in scope
195
+ ITestService resolvedInScope ;
196
+ using ( var scope = _serviceProvider . CreateScope ( ) )
197
+ {
198
+ resolvedInScope = scope . ServiceProvider . GetRequiredService < ITestService > ( ) ;
199
+ }
200
+
201
+ shouldResolve = true ;
202
+ await task ;
203
+ Assert . NotNull ( resolvedInThread ) ;
204
+ Assert . NotNull ( resolvedOutsideScope ) ;
205
+ Assert . NotNull ( resolvedInScope ) ;
206
+
207
+ Assert . NotEqual ( resolvedInScope , resolvedOutsideScope ) ;
208
+ Assert . NotEqual ( resolvedInScope , resolvedInThread ) ;
209
+ Assert . Equal ( resolvedOutsideScope , resolvedInThread ) ;
210
+ }
211
+
212
+ private class QueueUserWorkItemWorker : IThreadPoolWorkItem
213
+ {
214
+ private readonly IServiceProvider _provider ;
215
+ private readonly TaskCompletionSource _taskCompletionSource ;
216
+
217
+ public QueueUserWorkItemWorker ( IServiceProvider provider , TaskCompletionSource taskCompletionSource )
218
+ {
219
+ _provider = provider ;
220
+ _taskCompletionSource = taskCompletionSource ;
221
+ }
222
+
223
+ public ITestService ResolvedOutsideScope { get ; private set ; }
224
+ public ITestService ResolvedInScope { get ; private set ; }
225
+ public Exception ExecuteException { get ; private set ; }
226
+
227
+ public void Execute ( )
228
+ {
229
+ try
230
+ {
231
+ ResolvedOutsideScope = _provider . GetService < ITestService > ( ) ;
232
+ using ( var scope = _provider . CreateScope ( ) )
233
+ {
234
+ ResolvedInScope = scope . ServiceProvider . GetRequiredService < ITestService > ( ) ;
235
+ }
236
+ }
237
+ catch ( Exception ex )
238
+ {
239
+ ExecuteException = ex ;
240
+ }
241
+
242
+ _taskCompletionSource . SetResult ( ) ;
243
+ }
244
+ }
245
+
39
246
protected abstract IServiceCollection GetServiceCollection ( ) ;
40
247
41
248
protected abstract IServiceProvider BuildServiceProvider ( IServiceCollection serviceCollection ) ;
42
249
43
- public void Dispose ( )
44
- {
45
- Dispose ( true ) ;
46
- GC . SuppressFinalize ( this ) ;
47
- }
48
-
49
- protected virtual void Dispose ( bool disposing )
50
- {
51
- if ( disposing )
52
- {
53
- // Dispose managed resources
54
- if ( _serviceProvider is IDisposable disposable )
55
- {
56
- disposable . Dispose ( ) ;
57
- }
58
- }
59
- // Dispose unmanaged resources
60
- }
250
+ public void Dispose ( )
251
+ {
252
+ Dispose ( true ) ;
253
+ GC . SuppressFinalize ( this ) ;
254
+ }
255
+
256
+ protected virtual void Dispose ( bool disposing )
257
+ {
258
+ if ( disposing )
259
+ {
260
+ // Dispose managed resources
261
+ if ( _serviceProvider is IDisposable disposable )
262
+ {
263
+ disposable . Dispose ( ) ;
264
+ }
265
+ }
266
+ // Dispose unmanaged resources
267
+ }
61
268
}
62
269
63
270
public class RealCustomAssumptionTests : CustomAssumptionTests
@@ -75,6 +282,8 @@ protected override IServiceProvider BuildServiceProvider(IServiceCollection serv
75
282
76
283
public class CastleWindsorCustomAssumptionTests : CustomAssumptionTests
77
284
{
285
+ private IWindsorContainer _container ;
286
+
78
287
protected override IServiceCollection GetServiceCollection ( )
79
288
{
80
289
return new TestServiceCollection ( ) ;
@@ -83,8 +292,64 @@ protected override IServiceCollection GetServiceCollection()
83
292
protected override IServiceProvider BuildServiceProvider ( IServiceCollection serviceCollection )
84
293
{
85
294
var factory = new WindsorServiceProviderFactory ( ) ;
86
- var container = factory . CreateBuilder ( serviceCollection ) ;
87
- return factory . CreateServiceProvider ( container ) ;
295
+ _container = factory . CreateBuilder ( serviceCollection ) ;
296
+ return factory . CreateServiceProvider ( _container ) ;
297
+ }
298
+
299
+ [ Fact ]
300
+ public void Try_to_resolve_scoped_directly_with_castle_windsor_container ( )
301
+ {
302
+ var serviceCollection = GetServiceCollection ( ) ;
303
+ serviceCollection . AddScoped < ITestService , TestService > ( ) ;
304
+ var provider = BuildServiceProvider ( serviceCollection ) ;
305
+
306
+ //resolved outside scope
307
+ ITestService resolvedOutsideScope = _container . Resolve < ITestService > ( ) ;
308
+ Assert . NotNull ( resolvedOutsideScope ) ;
309
+
310
+ // resolve in scope
311
+ ITestService resolvedInScope ;
312
+ using ( var scope = provider . CreateScope ( ) )
313
+ {
314
+ resolvedInScope = _container . Resolve < ITestService > ( ) ;
315
+ }
316
+ Assert . NotNull ( resolvedInScope ) ;
317
+ Assert . NotEqual ( resolvedInScope , resolvedOutsideScope ) ;
318
+
319
+ ITestService resolvedAgainOutsideScope = _container . Resolve < ITestService > ( ) ;
320
+ Assert . NotNull ( resolvedAgainOutsideScope ) ;
321
+ Assert . Equal ( resolvedOutsideScope , resolvedAgainOutsideScope ) ;
322
+ }
323
+
324
+ [ Fact ]
325
+ public void TryToResolveScopedInOtherThread ( )
326
+ {
327
+ var serviceCollection = GetServiceCollection ( ) ;
328
+ serviceCollection . AddScoped < ITestService , TestService > ( ) ;
329
+ var provider = BuildServiceProvider ( serviceCollection ) ;
330
+
331
+ var task = Task . Run ( ( ) =>
332
+ {
333
+ //resolved outside scope
334
+ ITestService resolvedOutsideScope = _container . Resolve < ITestService > ( ) ;
335
+ Assert . NotNull ( resolvedOutsideScope ) ;
336
+
337
+ // resolve in scope
338
+ ITestService resolvedInScope ;
339
+ using ( var scope = provider . CreateScope ( ) )
340
+ {
341
+ resolvedInScope = _container . Resolve < ITestService > ( ) ;
342
+ }
343
+ Assert . NotNull ( resolvedInScope ) ;
344
+ Assert . NotEqual ( resolvedInScope , resolvedOutsideScope ) ;
345
+
346
+ ITestService resolvedAgainOutsideScope = _container . Resolve < ITestService > ( ) ;
347
+ Assert . NotNull ( resolvedAgainOutsideScope ) ;
348
+ Assert . Equal ( resolvedOutsideScope , resolvedAgainOutsideScope ) ;
349
+ return true ;
350
+ } ) ;
351
+
352
+ Assert . True ( task . Result ) ;
88
353
}
89
354
}
90
355
0 commit comments