Skip to content

Instantly share code, notes, and snippets.

@flq
Last active July 8, 2021 10:23
Show Gist options
  • Save flq/a4ee6064cec34adbf08632796fd75904 to your computer and use it in GitHub Desktop.
Save flq/a4ee6064cec34adbf08632796fd75904 to your computer and use it in GitHub Desktop.
Utility classes for named dependencies (,NET Core, C# 9)
using System;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
namespace DIPlaygroundWebApp.ServiceCollectionInfrastructure
{
public static class AssemblyPoolRegistrar
{
public static AssembliesForRegistration UseBulkRegistration(this IServiceCollection svcCollection,
params Assembly[] assemblies) =>
new(svcCollection, assemblies);
public class AssembliesForRegistration
{
private readonly Assembly[] _assemblies;
private readonly IServiceCollection _svcCollection;
public AssembliesForRegistration(IServiceCollection svcCollection, params Assembly[] assemblies)
{
_svcCollection = svcCollection;
_assemblies = assemblies;
}
public AssembliesForRegistration AddAllTypesOf<T>(ServiceLifetime lifeTime = ServiceLifetime.Transient,
Func<TypeInfo, string> naming = null)
{
var candidates = _assemblies.SelectMany(a => a.DefinedTypes)
.Where(t => t.InstantiableAndImplementing<T>())
.ToList();
var targetType = typeof(T);
foreach (var candidate in candidates)
_svcCollection.Add(new ServiceDescriptor(targetType, candidate, lifeTime));
if (naming == null) return this;
var namingDictionary = candidates.ToDictionary(naming);
NamedLookupInfo.RegisterMap(targetType, namingDictionary);
// register all concrete types
foreach (var implementationType in namingDictionary.Values)
_svcCollection.Add(new ServiceDescriptor(implementationType, implementationType, lifeTime));
_svcCollection.Add(new ServiceDescriptor(typeof(INamedLookup<T>), typeof(NamedLookup<T>), lifeTime));
return this;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Reflection;
namespace DIPlaygroundWebApp.ServiceCollectionInfrastructure
{
public interface INamedLookup<out T>
{
T this[string name] { get; }
}
public class NamedLookup<T> : INamedLookup<T>
{
private readonly IReadOnlyDictionary<string, TypeInfo> _serviceMap;
private readonly IServiceProvider _serviceProvider;
public NamedLookup(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
if (!NamedLookupInfo.TryGetValue(typeof(T), out _serviceMap))
throw new ArgumentException($"There is no service map for return type {typeof(T).Name} registered");
}
public T this[string name] => _serviceMap.TryGetValue(name, out var serviceType)
? (T) _serviceProvider.GetService(serviceType)
: default;
}
}
using System;
using System.Collections.Generic;
using System.Reflection;
namespace DIPlaygroundWebApp.ServiceCollectionInfrastructure
{
public static class NamedLookupInfo
{
private static readonly Dictionary<Type, IReadOnlyDictionary<string, TypeInfo>> ServiceMaps = new();
public static void RegisterMap(Type serviceType, Dictionary<string, TypeInfo> serviceMap)
{
ServiceMaps.Add(serviceType, serviceMap);
}
public static bool TryGetValue(Type serviceType, out IReadOnlyDictionary<string, TypeInfo> serviceMap)
{
return ServiceMaps.TryGetValue(serviceType, out serviceMap);
}
}
}
using System.Linq;
using System.Reflection;
namespace DIPlaygroundWebApp.ServiceCollectionInfrastructure
{
public static class ReflectionExtensions
{
public static bool InstantiableAndImplementing<TInterface>(this TypeInfo typeInfo)
{
return !typeInfo.IsAbstract && typeInfo.ImplementedInterfaces.Contains(typeof(TInterface));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment