Skip to content

Commit

Permalink
Fix castleproject#646: fix scope and scope-accessor implementation
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rvdginste authored and gavinschultz committed Sep 12, 2023
1 parent a9d6785 commit b65b43f
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 178 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public static ComponentRegistration<TService> ScopedToNetServiceScope<TService>(
public static ComponentRegistration<TService> LifestyleNetTransient<TService>(this ComponentRegistration<TService> registration) where TService : class
{
return registration
.Attribute(ExtensionContainerScopeBase.TransientMarker).Eq(Boolean.TrueString)
.Attribute(ExtensionContainerScope.TransientMarker).Eq(Boolean.TrueString)
.LifeStyle.ScopedToNetServiceScope(); //.NET core expects new instances but release on scope dispose
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@

namespace Castle.Windsor.Extensions.DependencyInjection.Scope
{
internal class ExtensionContainerRootScope : ExtensionContainerScopeBase
internal class ExtensionContainerRootScope : ExtensionContainerScope
{

private ExtensionContainerRootScope() : base(null, null)
{
}

public static ExtensionContainerRootScope BeginRootScope()
{
var scope = new ExtensionContainerRootScope();
ExtensionContainerScopeCache.Current = scope;
current.Value = scope;
return scope;
}

internal override ExtensionContainerScopeBase RootScope => this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@ internal class ExtensionContainerRootScopeAccessor : IScopeAccessor
{
public ILifetimeScope GetScope(CreationContext context)
{
return ExtensionContainerScopeCache.Current.RootScope ?? throw new InvalidOperationException("No root scope available");
if (ExtensionContainerScope.Current == null)
{
throw new InvalidOperationException("No root scope");
}

if (ExtensionContainerScope.Current.RootScope == null)
{
throw new InvalidOperationException("No root scope");
}

return ExtensionContainerScope.Current.RootScope;
}

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

namespace Castle.Windsor.Extensions.DependencyInjection.Scope
{
internal class ExtensionContainerScope : ExtensionContainerScopeBase
using System;
using System.Threading;

using Castle.Core;
using Castle.MicroKernel;
using Castle.MicroKernel.Lifestyle.Scoped;

internal class ExtensionContainerScope : ILifetimeScope, IDisposable
{
private readonly ExtensionContainerScopeBase parent;
public static ExtensionContainerScope Current => current.Value;
public static string TransientMarker = "Transient";
protected static readonly AsyncLocal<ExtensionContainerScope> current = new AsyncLocal<ExtensionContainerScope>();
private readonly ExtensionContainerScope parent;
private readonly ExtensionContainerRootScope rootScope;
private readonly IScopeCache scopeCache;

protected ExtensionContainerScope()
protected ExtensionContainerScope(
ExtensionContainerScope parent,
ExtensionContainerRootScope rootScope)
{
parent = ExtensionContainerScopeCache.Current;
scopeCache = new ScopeCache();
this.parent = parent ?? rootScope;
this.rootScope = rootScope;
}

internal override ExtensionContainerScopeBase RootScope { get; set; }

public ExtensionContainerRootScope RootScope
=> this as ExtensionContainerRootScope ?? rootScope;

internal static ExtensionContainerScopeBase BeginScope()
public static ExtensionContainerScope BeginScope(ExtensionContainerScope parent, ExtensionContainerRootScope rootScope)
{
var scope = new ExtensionContainerScope { RootScope = ExtensionContainerScopeCache.Current.RootScope };
ExtensionContainerScopeCache.Current = scope;
if (rootScope == null)
throw new ArgumentNullException(nameof(rootScope));

var scope = new ExtensionContainerScope(parent, rootScope);
current.Value = scope;
return scope;
}

public override void Dispose()
public void Dispose()
{
var disposableCache = scopeCache as IDisposable;
if (disposableCache != null)
{
disposableCache.Dispose();
}

current.Value = parent;
}

public Burden GetCachedInstance(ComponentModel model, ScopedInstanceActivationCallback createInstance)
{
if (ExtensionContainerScopeCache.current.Value == this)
lock (scopeCache)
{
// Add transient's burden to scope so it gets released
if (model.Configuration.Attributes.Get(TransientMarker) == bool.TrueString)
{
var transientBurden = createInstance((_) => {});
scopeCache[transientBurden] = transientBurden;
return transientBurden;
}

var scopedBurden = scopeCache[model];
if (scopedBurden != null)
{
return scopedBurden;
}
scopedBurden = createInstance((_) => {});
scopeCache[model] = scopedBurden;
return scopedBurden;
}
}

/// <summary>
/// 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
/// </summary>
internal class ForcedScope : IDisposable
{
private readonly ExtensionContainerScope previousScope;
public ForcedScope(ExtensionContainerScope scope)
{
previousScope = ExtensionContainerScope.Current;
ExtensionContainerScope.current.Value = scope;
}
public void Dispose()
{
ExtensionContainerScopeCache.current.Value = parent;
ExtensionContainerScope.current.Value = previousScope;
}
base.Dispose();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,20 @@

namespace Castle.Windsor.Extensions.DependencyInjection.Scope
{
using System;

using Castle.MicroKernel.Context;
using Castle.MicroKernel.Lifestyle.Scoped;

internal class ExtensionContainerScopeAccessor : IScopeAccessor
{
public ILifetimeScope GetScope(CreationContext context)
{
return ExtensionContainerScopeCache.Current;
if(ExtensionContainerScope.Current == null)
{
throw new InvalidOperationException("No scope available");
}
return ExtensionContainerScope.Current;
}

public void Dispose()
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,34 @@
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Castle.Windsor.Extensions.DependencyInjection.Scope
namespace Castle.Windsor.Extensions.DependencyInjection
{
using System;

using Microsoft.Extensions.DependencyInjection;

internal class ServiceScope : IServiceScope
{
private readonly IDisposable scope;
private readonly IServiceProvider serviceProvider;

public ServiceScope(IDisposable windsorScope, IServiceProvider serviceProvider)
{
scope = windsorScope ?? throw new ArgumentNullException(nameof(scope));
ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
if(windsorScope == null)
{
throw new ArgumentNullException(nameof(scope));
}

if(serviceProvider == null)
{
throw new ArgumentNullException(nameof(serviceProvider));
}

this.scope = windsorScope;
this.serviceProvider = serviceProvider;
}

public IServiceProvider ServiceProvider { get; }
public IServiceProvider ServiceProvider => serviceProvider;

public void Dispose()
{
Expand Down
Loading

0 comments on commit b65b43f

Please sign in to comment.