任何方式来命名相同类型的倍数?

时间:2018-02-16 06:59:57

标签: c# dependency-injection .net-core

注意:使用.NET Core 2.0 [Microsoft.Extensions.DependencyInjection]。

这是我想做的事情:

IServiceCollection collection = new ServiceCollection();
collection.AddSingleton<IMyReusableViewModel, MyReusableViewModel>(nameof(View1));
collection.AddSingleton<IMyReusableViewModel, MyReusableViewModel>(nameof(View2));

但我无法找到一种方法来命名添加到集合中的服务。如果在未指定名称的情况下重复使用的类型,则该集合似乎只是用新的服务覆盖现有服务。理想情况下,我可以重用一个类型,但可以通过提供名称来区分它。

  

请忽略人为的例子。

问:这可能吗?

1 个答案:

答案 0 :(得分:2)

鉴于ServiceDescriptor class没有Name属性或任何设置字符串标识符的方式,并且解析服务的类被标记为内部,我会说答案是的没有

但是,构建自己的扩展来伪造它并不是很困难。

NamedServiceDescriptor

class NamedServiceDescriptor
{
    public NamedServiceDescriptor(string name, Type serviceType)
    {
        this.Name = name;
        this.ServiceType = serviceType;
    }

    public string Name { get; private set; }
    public Type ServiceType { get; private set; }

    public override bool Equals(object obj)
    {
        if (!(obj is NamedServiceDescriptor))
            return false;

        var other = (NamedServiceDescriptor)obj;

        return Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase) &&
            ServiceType.Equals(other.ServiceType);
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode() ^ 
            ServiceType.GetHashCode();
    }
}

扩展方法

public static class ServiceCollectionExtensions
{
    internal static readonly IDictionary<NamedServiceDescriptor, Type> nameToTypeMap 
        = new ConcurrentDictionary<NamedServiceDescriptor, Type>();

    public static IServiceCollection AddSingleton<TService, TImplementation>(
        this IServiceCollection serviceCollection, 
        string name)
        where TService : class where TImplementation : class, TService
    {
        nameToTypeMap[new NamedServiceDescriptor(name, typeof(TService))] 
            = typeof(TImplementation);
        return serviceCollection.AddSingleton<TImplementation>();
    }
}

public static class ServiceProviderExtensions
{
    public static T GetService<T>(this IServiceProvider provider, string name)
    {
        if (provider == null)
            throw new ArgumentNullException(nameof(provider));
        if (string.IsNullOrEmpty(name))
            throw new ArgumentNullException(nameof(name));

        ServiceCollectionExtensions.nameToTypeMap.TryGetValue(
            new NamedServiceDescriptor(name, typeof(T)), out Type implementationType);
        return (T)provider.GetService(implementationType);
    }
}

用法

public interface IMyReusableViewModel { }
public class MyReusableViewModel1 : IMyReusableViewModel { }
public class MyReusableViewModel2 : IMyReusableViewModel { }

IServiceCollection collection = new ServiceCollection();
collection.AddSingleton<IMyReusableViewModel, MyReusableViewModel1>("View1");
collection.AddSingleton<IMyReusableViewModel, MyReusableViewModel2>("View2");


public class MyService
{
    private readonly IServiceProvider provider;

    public MyService(IServiceProvider provider)
    {
        this.provider = provider;
    }

    public void DoSomething()
    {
        var view1 = provider.GetService<IMyReusableViewModel>("View1");
        var view2 = provider.GetService<IMyReusableViewModel>("View2");

        // ...
    }
}
  

注意:那就是说,我不推荐这种做法。如果您需要此类功能,则表明应用程序设计不足。诸如Abstract FactoryStrategy之类的设计模式可能是填充空白而不诉诸命名类型注册或将容器滥用为Service Locator所需的。

或者,您可以使用支持此功能的第三方DI容器。