Skip to content

Instantly share code, notes, and snippets.

Created May 14, 2010 09:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/400979 to your computer and use it in GitHub Desktop.
Save anonymous/400979 to your computer and use it in GitHub Desktop.
Scoped lifestyle
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