Created
May 14, 2010 09:26
-
-
Save anonymous/400979 to your computer and use it in GitHub Desktop.
Scoped lifestyle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Castle.Windsor.Tests | |
{ | |
using System; | |
using System.Collections.Generic; | |
using Castle.Core; | |
using Castle.MicroKernel; | |
using Castle.MicroKernel.Context; | |
using Castle.MicroKernel.Lifestyle; | |
using Castle.MicroKernel.Registration; | |
using Castle.MicroKernel.Registration.Lifestyle; | |
public static class LifestyleExtension | |
{ | |
public static IDisposable BeginScope(this IWindsorContainer container) | |
{ | |
return new LifestyleScope(container.Kernel.GetSubSystem("scope") as IScopeManager); | |
} | |
public static ComponentRegistration<T> Scoped<T>(this LifestyleGroup<T> @group) | |
{ | |
return group.Custom<ScopedLifestyle>(); | |
} | |
} | |
public class ScopeSubsystem : AbstractSubSystem, IScopeManager | |
{ | |
private readonly Stack<LifestyleScope> scopes = new Stack<LifestyleScope>(); | |
public void EndCurrentScope() | |
{ | |
scopes.Pop(); | |
} | |
public void EnterScope(LifestyleScope scope) | |
{ | |
if (scope == null) | |
{ | |
throw new ArgumentNullException("scope"); | |
} | |
scopes.Push(scope); | |
} | |
public LifestyleScope GetCurrentScope() | |
{ | |
return scopes.Peek(); | |
} | |
} | |
public interface IScopeManager | |
{ | |
void EndCurrentScope(); | |
void EnterScope(LifestyleScope scope); | |
LifestyleScope GetCurrentScope(); | |
} | |
public class LifestyleScope : IDisposable | |
{ | |
private readonly IDictionary<ScopedLifestyle, object> cache = new Dictionary<ScopedLifestyle, object>(); | |
private readonly IScopeManager scope; | |
public LifestyleScope(IScopeManager scope) | |
{ | |
this.scope = scope; | |
scope.EnterScope(this); | |
} | |
public void AddComponent(ScopedLifestyle id, object component) | |
{ | |
cache.Add(id, component); | |
} | |
public object GetComponent(ScopedLifestyle id) | |
{ | |
object instance; | |
cache.TryGetValue(id, out instance); | |
return instance; | |
} | |
public bool HasComponent(ScopedLifestyle id) | |
{ | |
return cache.ContainsKey(id); | |
} | |
public void Dispose() | |
{ | |
scope.EndCurrentScope(); | |
foreach (var cacheEntry in cache) | |
{ | |
cacheEntry.Key.Evict(cacheEntry.Value); | |
} | |
cache.Clear(); | |
} | |
} | |
public class ScopedLifestyle : AbstractLifestyleManager | |
{ | |
private bool evicting; | |
private IScopeManager manager; | |
public override void Dispose() | |
{ | |
var current = GetCurrentScope(); | |
if (current == null) | |
{ | |
return; | |
} | |
var instance = current.GetComponent(this); | |
if (instance == null) | |
{ | |
return; | |
} | |
Evict(instance); | |
} | |
public override void Init(IComponentActivator componentActivator, IKernel kernel, ComponentModel model) | |
{ | |
base.Init(componentActivator, kernel, model); | |
manager = kernel.GetSubSystem("scope") as IScopeManager; | |
} | |
public override bool Release(object instance) | |
{ | |
// Since this method is called by the kernel when an external | |
// request to release the component is made, it must do nothing | |
// to ensure the component is available during the duration of | |
// the web request. An internal Evict method is provided to | |
// allow the actual releasing of the component at the end of | |
// the web request. | |
if (evicting == false) | |
{ | |
return false; | |
} | |
return base.Release(instance); | |
} | |
public override object Resolve(CreationContext context) | |
{ | |
var scope = GetCurrentScope(); | |
if (scope == null) | |
{ | |
throw new InvalidOperationException("No scope"); | |
} | |
if (scope.HasComponent(this)) | |
{ | |
return scope.GetComponent(this); | |
} | |
var component = base.Resolve(context); | |
scope.AddComponent(this, component); | |
return component; | |
} | |
internal void Evict(object instance) | |
{ | |
using (new EvictionScope(this)) | |
{ | |
// that's not really thread safe, should we care about it? | |
Kernel.ReleaseComponent(instance); | |
} | |
} | |
private LifestyleScope GetCurrentScope() | |
{ | |
return manager.GetCurrentScope(); | |
} | |
#region Nested type: EvictionScope | |
private class EvictionScope : IDisposable | |
{ | |
private readonly ScopedLifestyle owner; | |
public EvictionScope(ScopedLifestyle owner) | |
{ | |
this.owner = owner; | |
this.owner.evicting = true; | |
} | |
public void Dispose() | |
{ | |
owner.evicting = false; | |
} | |
} | |
#endregion | |
} | |
public interface IRepository | |
{ | |
ISession Session { get; } | |
} | |
public interface ISession : IDisposable | |
{ | |
bool IsDisposed { get; } | |
} | |
public class Session : ISession | |
{ | |
public bool IsDisposed { get; set; } | |
public void Dispose() | |
{ | |
IsDisposed = true; | |
} | |
} | |
public class Repository1 : IRepository | |
{ | |
public Repository1(ISession session) | |
{ | |
Session = session; | |
} | |
public ISession Session { get; private set; } | |
} | |
public class Repository2 : IRepository | |
{ | |
public Repository2(ISession session) | |
{ | |
Session = session; | |
} | |
public ISession Session { get; private set; } | |
} | |
public class Model1 | |
{ | |
public Model1(IRepository first, IRepository second) | |
{ | |
First = first; | |
Second = second; | |
} | |
public IRepository First { get; private set; } | |
public IRepository Second { get; private set; } | |
} | |
public class Model2 | |
{ | |
public Model2(IRepository second) | |
{ | |
Second = second; | |
} | |
public IRepository Second { get; private set; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment