Хотя, кажется, @Miguel A. Arilla четко указал на это, и я проголосовал за него, я создал поверх его полезного решения еще одно решение, которое выглядит аккуратно, но требует гораздо больше работы.
Это определенно зависит от вышеуказанного решения. В общем, я создал нечто похожее Func<string, IService>>и назвал это IServiceAccessorинтерфейсом, а затем мне пришлось добавить еще несколько расширений IServiceCollectionкак таковых:
public static IServiceCollection AddSingleton<TService, TImplementation, TServiceAccessor>(
this IServiceCollection services,
string instanceName
)
where TService : class
where TImplementation : class, TService
where TServiceAccessor : class, IServiceAccessor<TService>
{
services.AddSingleton<TService, TImplementation>();
services.AddSingleton<TServiceAccessor>();
var provider = services.BuildServiceProvider();
var implementationInstance = provider.GetServices<TService>().Last();
var accessor = provider.GetServices<TServiceAccessor>().First();
var serviceDescriptors = services.Where(d => d.ServiceType == typeof(TServiceAccessor));
while (serviceDescriptors.Any())
{
services.Remove(serviceDescriptors.First());
}
accessor.SetService(implementationInstance, instanceName);
services.AddSingleton<TServiceAccessor>(prvd => accessor);
return services;
}
Accessor службы выглядит так:
public interface IServiceAccessor<TService>
{
void Register(TService service,string name);
TService Resolve(string name);
}
В результате вы сможете зарегистрировать сервисы с именами или именованными экземплярами, как мы это делали с другими контейнерами. Например:
services.AddSingleton<IEncryptionService, SymmetricEncryptionService, EncyptionServiceAccessor>("Symmetric");
services.AddSingleton<IEncryptionService, AsymmetricEncryptionService, EncyptionServiceAccessor>("Asymmetric");
Пока этого достаточно, но чтобы завершить работу, лучше добавить больше методов расширения, чтобы охватить все типы регистраций, следуя тому же подходу.
Был еще один пост о stackoverflow, но я не могу найти его, где автор подробно объяснил, почему эта функция не поддерживается и как ее обойти, в основном аналогично тому, что сказал @Miguel. Это был хороший пост, хотя я не согласен с каждым пунктом, потому что я думаю, что есть ситуации, когда вам действительно нужны именованные экземпляры. Я опубликую эту ссылку здесь, как только найду ее снова.
На самом деле вам не нужно передавать этот селектор или аксессор:
Я использую следующий код в своем проекте, и до сих пор он работал хорошо.
/// <summary>
/// Adds the singleton.
/// </summary>
/// <typeparam name="TService">The type of the t service.</typeparam>
/// <typeparam name="TImplementation">The type of the t implementation.</typeparam>
/// <param name="services">The services.</param>
/// <param name="instanceName">Name of the instance.</param>
/// <returns>IServiceCollection.</returns>
public static IServiceCollection AddSingleton<TService, TImplementation>(
this IServiceCollection services,
string instanceName
)
where TService : class
where TImplementation : class, TService
{
var provider = services.BuildServiceProvider();
var implementationInstance = provider.GetServices<TService>().LastOrDefault();
if (implementationInstance.IsNull())
{
services.AddSingleton<TService, TImplementation>();
provider = services.BuildServiceProvider();
implementationInstance = provider.GetServices<TService>().Single();
}
return services.RegisterInternal(instanceName, provider, implementationInstance);
}
private static IServiceCollection RegisterInternal<TService>(this IServiceCollection services,
string instanceName, ServiceProvider provider, TService implementationInstance)
where TService : class
{
var accessor = provider.GetServices<IServiceAccessor<TService>>().LastOrDefault();
if (accessor.IsNull())
{
services.AddSingleton<ServiceAccessor<TService>>();
provider = services.BuildServiceProvider();
accessor = provider.GetServices<ServiceAccessor<TService>>().Single();
}
else
{
var serviceDescriptors = services.Where(d => d.ServiceType == typeof(IServiceAccessor<TService>));
while (serviceDescriptors.Any())
{
services.Remove(serviceDescriptors.First());
}
}
accessor.Register(implementationInstance, instanceName);
services.AddSingleton<TService>(prvd => implementationInstance);
services.AddSingleton<IServiceAccessor<TService>>(prvd => accessor);
return services;
}
//
// Summary:
// Adds a singleton service of the type specified in TService with an instance specified
// in implementationInstance to the specified Microsoft.Extensions.DependencyInjection.IServiceCollection.
//
// Parameters:
// services:
// The Microsoft.Extensions.DependencyInjection.IServiceCollection to add the service
// to.
// implementationInstance:
// The instance of the service.
// instanceName:
// The name of the instance.
//
// Returns:
// A reference to this instance after the operation has completed.
public static IServiceCollection AddSingleton<TService>(
this IServiceCollection services,
TService implementationInstance,
string instanceName) where TService : class
{
var provider = services.BuildServiceProvider();
return RegisterInternal(services, instanceName, provider, implementationInstance);
}
/// <summary>
/// Registers an interface for a class
/// </summary>
/// <typeparam name="TInterface">The type of the t interface.</typeparam>
/// <param name="services">The services.</param>
/// <returns>IServiceCollection.</returns>
public static IServiceCollection As<TInterface>(this IServiceCollection services)
where TInterface : class
{
var descriptor = services.Where(d => d.ServiceType.GetInterface(typeof(TInterface).Name) != null).FirstOrDefault();
if (descriptor.IsNotNull())
{
var provider = services.BuildServiceProvider();
var implementationInstance = (TInterface)provider?.GetServices(descriptor?.ServiceType)?.Last();
services?.AddSingleton(implementationInstance);
}
return services;
}