Any way to name multiples of the same type?

后端 未结 1 1529
青春惊慌失措
青春惊慌失措 2021-01-27 07:24

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

Here\'s what I would like to do:

IServiceCollection collection = new ServiceCollect         


        
相关标签:
1条回答
  • 2021-01-27 07:50

    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.

    0 讨论(0)
提交回复
热议问题