有什么办法可以命名相同类型的倍数? [英] Any way to name multiples of the same type?

查看:72
本文介绍了有什么办法可以命名相同类型的倍数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我想做的是:

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

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


请忽略人为的示例。


问:这可能吗?

解决方案

考虑到 ServiceDescriptor类没有 Name 属性或任何设置字符串标识符的方式,并且用于解析服务的类都标记为内部,我想答案是




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



NamedServiceDescriptor



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

公共字符串名称{get;私人套装; }
public Type ServiceType {get;私人套装; }

公共替代布尔值Equals(object obj)
{
if(!(obj是NamedServiceDescriptor))
返回false;

var other =(NamedServiceDescriptor)obj;

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

公共重写int GetHashCode()
{
返回Name.GetHashCode()^
ServiceType.GetHashCode();
}
}



扩展方法



 公共静态类ServiceCollectionExtensions 
{
内部静态只读IDictionary< NamedServiceDescriptor,Type> nameToTypeMap
=新的ConcurrentDictionary< NamedServiceDescriptor,Type>();

公共静态IServiceCollection AddSingleton< TService,TImplementation>(
此IServiceCollection serviceCollection,
字符串名称)
其中TService:class其中TImplementation:class,TService
{
nameToTypeMap [new NamedServiceDescriptor(name,typeof(TService))]
= typeof(TImplementation);
return serviceCollection.AddSingleton< TImplementation>();
}
}

公共静态类ServiceProviderExtensions
{
公共静态T GetService< T>(此IServiceProvider提供者,字符串名称)
{
if(provider == null)
throw new ArgumentNullException(nameof(provider));
if(string.IsNullOrEmpty(name))
抛出新的ArgumentNullException(nameof(name));

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



用法



 公共接口IMyReusableViewModel {} 
公共类MyReusableViewModel1:IMyReusableViewModel {}
公共类MyReusableViewModel2:IMyReusableViewModel {}

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


公共类MyService
{
私人只读IServiceProvider提供程序;

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

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

// ...
}
}



< blockquote>

注意:也就是说,我不推荐这种方法。如果需要此类功能,则表明应用程序设计不充分。一种设计模式,例如抽象工厂可能需要采取策略,而无需诉诸命名类型注册或滥用容器作为服务定位器


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


Note: using .NET Core 2.0 [Microsoft.Extensions.DependencyInjection].

Here's what I would like to do:

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

But I can't figure out a way to name services added to the collection. If the type used repeats without a name specified, the collection appears to simply overwrite the existing service with the new one. Ideally, I would be able to reuse a type, but differentiate it by supplying a name.

Please ignore the contrived example.

Q: Is this possible?

解决方案

Given the fact that the ServiceDescriptor class doesn't have a Name property or any way to set a string identifier and the classes for resolving services are marked internal, I would say the answer is no.


However, it's not very difficult to build your own extensions to fake it.

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();
    }
}

Extension Methods

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);
    }
}

Usage

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");

        // ...
    }
}

NOTE: That said, I wouldn't recommend this approach. If you require such functionality, it is a sign that the application design is inadequate. A design pattern such as Abstract Factory or Strategy may be what is needed to fill the void without resorting to naming type registrations or abusing the container as a Service Locator.

Alternatively, you could use a 3rd party DI container that supports this functionality.

这篇关于有什么办法可以命名相同类型的倍数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆