Skip to content

Commit 1203337

Browse files
committed
Fix castleproject#646: fix scope and scope-accessor implementation
To fix issue castleproject#563 (support concurrently existing containers) a fix was created in castleproject#577. After the initial fix some refactorings were done on the scope implementation that were not correct. This commit changes the scope implementation back to the original implementation of @ltines, but with the support for concurrent containers.
1 parent 36db480 commit 1203337

11 files changed

+149
-178
lines changed

src/Castle.Windsor.Extensions.DependencyInjection/Extensions/WindsorExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public static ComponentRegistration<TService> ScopedToNetServiceScope<TService>(
3939
public static ComponentRegistration<TService> LifestyleNetTransient<TService>(this ComponentRegistration<TService> registration) where TService : class
4040
{
4141
return registration
42-
.Attribute(ExtensionContainerScopeBase.TransientMarker).Eq(Boolean.TrueString)
42+
.Attribute(ExtensionContainerScope.TransientMarker).Eq(Boolean.TrueString)
4343
.LifeStyle.ScopedToNetServiceScope(); //.NET core expects new instances but release on scope dispose
4444
}
4545

src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerRootScope.cs

+6-5
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,17 @@
1414

1515
namespace Castle.Windsor.Extensions.DependencyInjection.Scope
1616
{
17-
internal class ExtensionContainerRootScope : ExtensionContainerScopeBase
17+
internal class ExtensionContainerRootScope : ExtensionContainerScope
1818
{
19-
19+
private ExtensionContainerRootScope() : base(null, null)
20+
{
21+
}
22+
2023
public static ExtensionContainerRootScope BeginRootScope()
2124
{
2225
var scope = new ExtensionContainerRootScope();
23-
ExtensionContainerScopeCache.Current = scope;
26+
current.Value = scope;
2427
return scope;
2528
}
26-
27-
internal override ExtensionContainerScopeBase RootScope => this;
2829
}
2930
}

src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerRootScopeAccessor.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,17 @@ internal class ExtensionContainerRootScopeAccessor : IScopeAccessor
2323
{
2424
public ILifetimeScope GetScope(CreationContext context)
2525
{
26-
return ExtensionContainerScopeCache.Current.RootScope ?? throw new InvalidOperationException("No root scope available");
26+
if (ExtensionContainerScope.Current == null)
27+
{
28+
throw new InvalidOperationException("No root scope");
29+
}
30+
31+
if (ExtensionContainerScope.Current.RootScope == null)
32+
{
33+
throw new InvalidOperationException("No root scope");
34+
}
35+
36+
return ExtensionContainerScope.Current.RootScope;
2737
}
2838

2939
public void Dispose()
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2004-2020 Castle Project - http://www.castleproject.org/
1+
// Copyright 2004-2020 Castle Project - http://www.castleproject.org/
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -14,32 +14,93 @@
1414

1515
namespace Castle.Windsor.Extensions.DependencyInjection.Scope
1616
{
17-
internal class ExtensionContainerScope : ExtensionContainerScopeBase
17+
using System;
18+
using System.Threading;
19+
20+
using Castle.Core;
21+
using Castle.MicroKernel;
22+
using Castle.MicroKernel.Lifestyle.Scoped;
23+
24+
internal class ExtensionContainerScope : ILifetimeScope, IDisposable
1825
{
19-
private readonly ExtensionContainerScopeBase parent;
26+
public static ExtensionContainerScope Current => current.Value;
27+
public static string TransientMarker = "Transient";
28+
protected static readonly AsyncLocal<ExtensionContainerScope> current = new AsyncLocal<ExtensionContainerScope>();
29+
private readonly ExtensionContainerScope parent;
30+
private readonly ExtensionContainerRootScope rootScope;
31+
private readonly IScopeCache scopeCache;
2032

21-
protected ExtensionContainerScope()
33+
protected ExtensionContainerScope(
34+
ExtensionContainerScope parent,
35+
ExtensionContainerRootScope rootScope)
2236
{
23-
parent = ExtensionContainerScopeCache.Current;
37+
scopeCache = new ScopeCache();
38+
this.parent = parent ?? rootScope;
39+
this.rootScope = rootScope;
2440
}
2541

26-
internal override ExtensionContainerScopeBase RootScope { get; set; }
27-
42+
public ExtensionContainerRootScope RootScope
43+
=> this as ExtensionContainerRootScope ?? rootScope;
2844

29-
internal static ExtensionContainerScopeBase BeginScope()
45+
public static ExtensionContainerScope BeginScope(ExtensionContainerScope parent, ExtensionContainerRootScope rootScope)
3046
{
31-
var scope = new ExtensionContainerScope { RootScope = ExtensionContainerScopeCache.Current.RootScope };
32-
ExtensionContainerScopeCache.Current = scope;
47+
if (rootScope == null)
48+
throw new ArgumentNullException(nameof(rootScope));
49+
50+
var scope = new ExtensionContainerScope(parent, rootScope);
51+
current.Value = scope;
3352
return scope;
3453
}
3554

36-
public override void Dispose()
55+
public void Dispose()
56+
{
57+
var disposableCache = scopeCache as IDisposable;
58+
if (disposableCache != null)
59+
{
60+
disposableCache.Dispose();
61+
}
62+
63+
current.Value = parent;
64+
}
65+
66+
public Burden GetCachedInstance(ComponentModel model, ScopedInstanceActivationCallback createInstance)
3767
{
38-
if (ExtensionContainerScopeCache.current.Value == this)
68+
lock (scopeCache)
69+
{
70+
// Add transient's burden to scope so it gets released
71+
if (model.Configuration.Attributes.Get(TransientMarker) == bool.TrueString)
72+
{
73+
var transientBurden = createInstance((_) => {});
74+
scopeCache[transientBurden] = transientBurden;
75+
return transientBurden;
76+
}
77+
78+
var scopedBurden = scopeCache[model];
79+
if (scopedBurden != null)
80+
{
81+
return scopedBurden;
82+
}
83+
scopedBurden = createInstance((_) => {});
84+
scopeCache[model] = scopedBurden;
85+
return scopedBurden;
86+
}
87+
}
88+
89+
/// <summary>
90+
/// Forces a specific <see name="ExtensionContainerScope" /> for 'using' block. In .NET scope is tied to an instance of <see name="System.IServiceProvider" /> not a thread or async context
91+
/// </summary>
92+
internal class ForcedScope : IDisposable
93+
{
94+
private readonly ExtensionContainerScope previousScope;
95+
public ForcedScope(ExtensionContainerScope scope)
96+
{
97+
previousScope = ExtensionContainerScope.Current;
98+
ExtensionContainerScope.current.Value = scope;
99+
}
100+
public void Dispose()
39101
{
40-
ExtensionContainerScopeCache.current.Value = parent;
102+
ExtensionContainerScope.current.Value = previousScope;
41103
}
42-
base.Dispose();
43104
}
44105
}
45-
}
106+
}

src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeAccessor.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,20 @@
1414

1515
namespace Castle.Windsor.Extensions.DependencyInjection.Scope
1616
{
17+
using System;
18+
1719
using Castle.MicroKernel.Context;
1820
using Castle.MicroKernel.Lifestyle.Scoped;
1921

2022
internal class ExtensionContainerScopeAccessor : IScopeAccessor
2123
{
2224
public ILifetimeScope GetScope(CreationContext context)
2325
{
24-
return ExtensionContainerScopeCache.Current;
26+
if(ExtensionContainerScope.Current == null)
27+
{
28+
throw new InvalidOperationException("No scope available");
29+
}
30+
return ExtensionContainerScope.Current;
2531
}
2632

2733
public void Dispose()

src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeBase.cs

-66
This file was deleted.

src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeCache.cs

-31
This file was deleted.

src/Castle.Windsor.Extensions.DependencyInjection/Scope/ForcedScope.cs

-38
This file was deleted.

src/Castle.Windsor.Extensions.DependencyInjection/Scope/ServiceScope.cs renamed to src/Castle.Windsor.Extensions.DependencyInjection/ServiceScope.cs

+16-5
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,34 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
namespace Castle.Windsor.Extensions.DependencyInjection.Scope
15+
namespace Castle.Windsor.Extensions.DependencyInjection
1616
{
1717
using System;
18-
18+
1919
using Microsoft.Extensions.DependencyInjection;
2020

2121
internal class ServiceScope : IServiceScope
2222
{
2323
private readonly IDisposable scope;
24+
private readonly IServiceProvider serviceProvider;
2425

2526
public ServiceScope(IDisposable windsorScope, IServiceProvider serviceProvider)
2627
{
27-
scope = windsorScope ?? throw new ArgumentNullException(nameof(scope));
28-
ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
28+
if(windsorScope == null)
29+
{
30+
throw new ArgumentNullException(nameof(scope));
31+
}
32+
33+
if(serviceProvider == null)
34+
{
35+
throw new ArgumentNullException(nameof(serviceProvider));
36+
}
37+
38+
this.scope = windsorScope;
39+
this.serviceProvider = serviceProvider;
2940
}
3041

31-
public IServiceProvider ServiceProvider { get; }
42+
public IServiceProvider ServiceProvider => serviceProvider;
3243

3344
public void Dispose()
3445
{

0 commit comments

Comments
 (0)