@@ -19,14 +19,14 @@ namespace SolidToken.SpecFlow.DependencyInjection
1919{
2020 public class DependencyInjectionPlugin : IRuntimePlugin
2121 {
22- private static readonly ConcurrentDictionary < IServiceProvider , IContextManager > BindMapping =
22+ private static readonly ConcurrentDictionary < IServiceProvider , IContextManager > BindMappings =
2323 new ConcurrentDictionary < IServiceProvider , IContextManager > ( ) ;
24-
24+
2525 private static readonly ConcurrentDictionary < ISpecFlowContext , IServiceScope > ActiveServiceScopes =
2626 new ConcurrentDictionary < ISpecFlowContext , IServiceScope > ( ) ;
2727
2828 private readonly object _registrationLock = new object ( ) ;
29-
29+
3030 public void Initialize ( RuntimePluginEvents runtimePluginEvents , RuntimePluginParameters runtimePluginParameters , UnitTestProviderConfiguration unitTestProviderConfiguration )
3131 {
3232 runtimePluginEvents . CustomizeGlobalDependencies += CustomizeGlobalDependencies ;
@@ -46,7 +46,7 @@ private void CustomizeGlobalDependencies(object sender, CustomizeGlobalDependenc
4646 args . ObjectContainer . RegisterTypeAs < ServiceCollectionFinder , IServiceCollectionFinder > ( ) ;
4747 }
4848
49- // We store the service provider in the global container, we create it only once
49+ // We store the (MS) service provider in the global (BoDi) container, we create it only once.
5050 // It must be lazy (hence factory) because at this point we still don't have the bindings mapped.
5151 args . ObjectContainer . RegisterFactoryAs < RootServiceProviderContainer > ( ( ) =>
5252 {
@@ -70,7 +70,7 @@ private void CustomizeGlobalDependencies(object sender, CustomizeGlobalDependenc
7070 args . ObjectContainer . Resolve < IServiceCollectionFinder > ( ) ;
7171 }
7272 }
73-
73+
7474 private static void CustomizeFeatureDependenciesEventHandler ( object sender , CustomizeFeatureDependenciesEventArgs args )
7575 {
7676 // At this point we have the bindings, we can resolve the service provider, which will build it if it's the first time.
@@ -84,13 +84,22 @@ private static void CustomizeFeatureDependenciesEventHandler(object sender, Cust
8484 args . ObjectContainer . RegisterFactoryAs < IServiceProvider > ( ( ) =>
8585 {
8686 var scope = serviceProvider . CreateScope ( ) ;
87- BindMapping . TryAdd ( scope . ServiceProvider , args . ObjectContainer . Resolve < IContextManager > ( ) ) ;
87+ BindMappings . TryAdd ( scope . ServiceProvider , args . ObjectContainer . Resolve < IContextManager > ( ) ) ;
8888 ActiveServiceScopes . TryAdd ( args . ObjectContainer . Resolve < FeatureContext > ( ) , scope ) ;
8989 return scope . ServiceProvider ;
9090 } ) ;
9191 }
9292 }
9393
94+ private static void AfterFeaturePluginLifecycleEventHandler ( object sender , RuntimePluginAfterFeatureEventArgs eventArgs )
95+ {
96+ if ( ActiveServiceScopes . TryRemove ( eventArgs . ObjectContainer . Resolve < FeatureContext > ( ) , out var serviceScope ) )
97+ {
98+ BindMappings . TryRemove ( serviceScope . ServiceProvider , out _ ) ;
99+ serviceScope . Dispose ( ) ;
100+ }
101+ }
102+
94103 private static void CustomizeScenarioDependenciesEventHandler ( object sender , CustomizeScenarioDependenciesEventArgs args )
95104 {
96105 // At this point we have the bindings, we can resolve the service provider, which will build it if it's the first time.
@@ -103,26 +112,18 @@ private static void CustomizeScenarioDependenciesEventHandler(object sender, Cus
103112 args . ObjectContainer . RegisterFactoryAs < IServiceProvider > ( ( ) =>
104113 {
105114 var scope = serviceProvider . CreateScope ( ) ;
115+ BindMappings . TryAdd ( scope . ServiceProvider , args . ObjectContainer . Resolve < IContextManager > ( ) ) ;
106116 ActiveServiceScopes . TryAdd ( args . ObjectContainer . Resolve < ScenarioContext > ( ) , scope ) ;
107117 return scope . ServiceProvider ;
108118 } ) ;
109119 }
110120 }
111-
121+
112122 private static void AfterScenarioPluginLifecycleEventHandler ( object sender , RuntimePluginAfterScenarioEventArgs eventArgs )
113123 {
114124 if ( ActiveServiceScopes . TryRemove ( eventArgs . ObjectContainer . Resolve < ScenarioContext > ( ) , out var serviceScope ) )
115125 {
116- BindMapping . TryRemove ( serviceScope . ServiceProvider , out _ ) ;
117- serviceScope . Dispose ( ) ;
118- }
119- }
120-
121- private static void AfterFeaturePluginLifecycleEventHandler ( object sender , RuntimePluginAfterFeatureEventArgs eventArgs )
122- {
123- if ( ActiveServiceScopes . TryRemove ( eventArgs . ObjectContainer . Resolve < FeatureContext > ( ) , out var serviceScope ) )
124- {
125- BindMapping . TryRemove ( serviceScope . ServiceProvider , out _ ) ;
126+ BindMappings . TryRemove ( serviceScope . ServiceProvider , out _ ) ;
126127 serviceScope . Dispose ( ) ;
127128 }
128129 }
@@ -132,7 +133,7 @@ private static void RegisterProxyBindings(IObjectContainer objectContainer, ISer
132133 // Required for DI of binding classes that want container injections
133134 // While they can (and should) use the method params for injection, we can support it.
134135 // Note that in Feature mode, one can't inject "ScenarioContext", this can only be done from method params.
135-
136+
136137 // Bases on this: https://docs.specflow.org/projects/specflow/en/latest/Extend/Available-Containers-%26-Registrations.html
137138 // Might need to add more...
138139
@@ -157,7 +158,7 @@ private static void RegisterProxyBindings(IObjectContainer objectContainer, ISer
157158
158159 services . AddTransient ( sp =>
159160 {
160- var container = BindMapping . TryGetValue ( sp , out var ctx )
161+ var container = BindMappings . TryGetValue ( sp , out var ctx )
161162 ? ctx . ScenarioContext ? . ScenarioContainer ??
162163 ctx . FeatureContext ? . FeatureContainer ??
163164 ctx . TestThreadContext ? . TestThreadContainer ??
@@ -166,15 +167,15 @@ private static void RegisterProxyBindings(IObjectContainer objectContainer, ISer
166167
167168 return container . Resolve < ISpecFlowOutputHelper > ( ) ;
168169 } ) ;
169-
170- services . AddTransient ( sp => BindMapping [ sp ] ) ;
171- services . AddTransient ( sp => BindMapping [ sp ] . TestThreadContext ) ;
172- services . AddTransient ( sp => BindMapping [ sp ] . FeatureContext ) ;
173- services . AddTransient ( sp => BindMapping [ sp ] . ScenarioContext ) ;
174- services . AddTransient ( sp => BindMapping [ sp ] . TestThreadContext . TestThreadContainer . Resolve < ITestRunner > ( ) ) ;
175- services . AddTransient ( sp => BindMapping [ sp ] . TestThreadContext . TestThreadContainer . Resolve < ITestExecutionEngine > ( ) ) ;
176- services . AddTransient ( sp => BindMapping [ sp ] . TestThreadContext . TestThreadContainer . Resolve < IStepArgumentTypeConverter > ( ) ) ;
177- services . AddTransient ( sp => BindMapping [ sp ] . TestThreadContext . TestThreadContainer . Resolve < IStepDefinitionMatchService > ( ) ) ;
170+
171+ services . AddTransient ( sp => BindMappings [ sp ] ) ;
172+ services . AddTransient ( sp => BindMappings [ sp ] . TestThreadContext ) ;
173+ services . AddTransient ( sp => BindMappings [ sp ] . FeatureContext ) ;
174+ services . AddTransient ( sp => BindMappings [ sp ] . ScenarioContext ) ;
175+ services . AddTransient ( sp => BindMappings [ sp ] . TestThreadContext . TestThreadContainer . Resolve < ITestRunner > ( ) ) ;
176+ services . AddTransient ( sp => BindMappings [ sp ] . TestThreadContext . TestThreadContainer . Resolve < ITestExecutionEngine > ( ) ) ;
177+ services . AddTransient ( sp => BindMappings [ sp ] . TestThreadContext . TestThreadContainer . Resolve < IStepArgumentTypeConverter > ( ) ) ;
178+ services . AddTransient ( sp => BindMappings [ sp ] . TestThreadContext . TestThreadContainer . Resolve < IStepDefinitionMatchService > ( ) ) ;
178179 }
179180
180181 private class RootServiceProviderContainer
0 commit comments