Skip to content

Instantly share code, notes, and snippets.

@zpqrtbnk
Last active January 11, 2019 17:52
Show Gist options
  • Save zpqrtbnk/2667c45bddee97d8e39b7070aaf4aade to your computer and use it in GitHub Desktop.
Save zpqrtbnk/2667c45bddee97d8e39b7070aaf4aade to your computer and use it in GitHub Desktop.
Named Services for MS.DI
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
namespace MSDI
{
public static class NamedServiceExtensions
{
private static readonly object _locker = new object();
private static readonly Dictionary<string, string> _uniqueNames = new Dictionary<string, string>();
private static readonly Dictionary<string, Type> _uniqueNameTypes = new Dictionary<string, Type>();
private static readonly Dictionary<string, Type> _uniqueTypes = new Dictionary<string, Type>();
private static Type UniqueType(string name)
{
if (_uniqueNameTypes.TryGetValue(name, out var type))
return type;
var typeSignature = "MSDI.NamedService." + name;
var assemblyName = new AssemblyName(typeSignature);
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
var typeBuilder = moduleBuilder.DefineType(typeSignature,
TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
null);
return _uniqueNameTypes[name] = typeBuilder.CreateType();
}
private static Type UniqueType<TService>(string name)
{
lock (_locker) // there's probably a better way but bah
{
if (!_uniqueNames.TryGetValue(name, out var uniqueName))
uniqueName = _uniqueNames[name] = Guid.NewGuid().ToString("N");
if (_uniqueTypes.TryGetValue(uniqueName, out var uniqueType))
return uniqueType;
return _uniqueTypes[name] = typeof(W<,>).MakeGenericType(typeof(TService), UniqueType(uniqueName));
}
}
private static object Create<TImplementation>(IServiceProvider provider, Type type)
{
var implementation = ActivatorUtilities.CreateInstance<TImplementation>(provider);
var ctor = type.GetConstructors().First();
return ctor.Invoke(new object[] { implementation });
}
public static void AddNamedService<TService, TImplementation>(this IServiceCollection serviceCollection, string name, ServiceLifetime lifetime = ServiceLifetime.Transient)
where TService : class
{
var type = UniqueType<TService>(name);
serviceCollection.Add(new ServiceDescriptor(type, provider => Create<TImplementation>(provider, type), lifetime));
}
public static TService GetNamedService<TService>(this IServiceProvider serviceProvider, string name)
where TService : class
{
return ((W<TService>)serviceProvider.GetService(UniqueType<TService>(name))).Service;
}
}
public class W<TService> : IDisposable
where TService : class
{
public W(TService service)
{
Service = service;
}
public TService Service { get; }
public void Dispose()
{
if (Service is IDisposable disposable)
disposable.Dispose();
}
}
public class W<TService, TName> : W<TService>
where TService : class
{
public W(TService service)
: base(service)
{ }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment