Skip to content

Instantly share code, notes, and snippets.

@Tornhoof
Last active August 10, 2016 14:32
Show Gist options
  • Save Tornhoof/9014ed743f1edce0abe4afb49f671783 to your computer and use it in GitHub Desktop.
Save Tornhoof/9014ed743f1edce0abe4afb49f671783 to your computer and use it in GitHub Desktop.
Tenant lifestyle
using System;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
using SimpleInjector;
namespace PerTenantLifestyle
{
/// <summary>
/// Special Lifestyle for per tenant objects, basically a singleton per tenant pattern
/// Use this lifestyle if you need per tenant services, e.g. per tenant cache etc.
/// </summary>
internal class PerTenantLifestyle : Lifestyle
{
public PerTenantLifestyle() : base("PerTenant")
{
}
protected override int Length => 500;
protected override Registration CreateRegistrationCore<TService, TImplementation>(Container container)
{
return new PerTenantLifestyleRegistration<TService, TImplementation>(this, container);
}
protected override Registration CreateRegistrationCore<TService>(Func<TService> instanceCreator, Container container)
{
return new PerTenantLifestyleRegistration<TService>(this, container, instanceCreator);
}
private sealed class PerTenantLifestyleRegistration<TService> : PerTenantLifestyleRegistrationBase<TService>
where TService : class
{
private readonly Func<TService> m_InstanceCreator;
public PerTenantLifestyleRegistration(Lifestyle lifestyle,
Container container,
Func<TService> instanceCreator)
: base(lifestyle, container)
{
m_InstanceCreator = instanceCreator;
}
public override Type ImplementationType => typeof(TService);
protected override Func<TService> BuildTransientDelegate()
{
return BuildTransientDelegate(m_InstanceCreator);
}
}
private class PerTenantLifestyleRegistration<TService, TImplementation> : PerTenantLifestyleRegistrationBase<TService>
where TImplementation : class, TService
where TService : class
{
internal PerTenantLifestyleRegistration(Lifestyle lifestyle, Container container)
: base(lifestyle, container)
{
}
public override Type ImplementationType => typeof(TImplementation);
protected override Func<TService> BuildTransientDelegate()
{
return BuildTransientDelegate<TService, TImplementation>();
}
}
private abstract class PerTenantLifestyleRegistrationBase : Registration
{
protected static readonly Action<Container, IDisposable> s_RegisterForDispose;
static PerTenantLifestyleRegistrationBase()
{
var mi = typeof(Container).GetMethod("RegisterForDisposal", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(IDisposable) }, null);
var lParam = Expression.Parameter(typeof(Container));
var rParam = Expression.Parameter(typeof(IDisposable));
var lambda = Expression.Lambda<Action<Container, IDisposable>>(Expression.Call(lParam, mi, rParam), lParam, rParam);
s_RegisterForDispose = lambda.Compile();
}
protected PerTenantLifestyleRegistrationBase(Lifestyle lifestyle, Container container) : base(lifestyle, container)
{
}
}
private abstract class PerTenantLifestyleRegistrationBase<TService> : PerTenantLifestyleRegistrationBase where TService : class
{
private readonly ConcurrentDictionary<Guid, Lazy<TService>> m_ConcurrentDictionary = new ConcurrentDictionary<Guid, Lazy<TService>>();
private readonly Expression<Func<TService>> m_Invoker;
protected PerTenantLifestyleRegistrationBase(Lifestyle lifestyle, Container container) : base(lifestyle, container)
{
m_Invoker = () => GetInstance();
}
public override Expression BuildExpression()
{
return Expression.Invoke(m_Invoker);
}
private TService GetInstance()
{
var tenantId = IdentityHelper.TenantId();
return m_ConcurrentDictionary.GetOrAdd(tenantId, guid => new Lazy<TService>(CreateInstanceWithNullCheck)).Value;
}
private TService CreateInstanceWithNullCheck()
{
var functor = BuildTransientDelegate();
var instance = functor();
if (instance == null)
{
throw new ActivationException($"Instance of {typeof(TService).FullName} is null.");
}
var disposable = instance as IDisposable;
if (disposable != null)
{
s_RegisterForDispose(Container, disposable);
}
return instance;
}
protected abstract Func<TService> BuildTransientDelegate();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment