How to register multiple implementations of the same interface in Asp.Net Core?

前端 未结 24 1946
不思量自难忘°
不思量自难忘° 2020-11-22 10:16

I have services that are derived from the same interface.

public interface IService { }
public class ServiceA : IService { }
public class ServiceB : IService         


        
24条回答
  •  清歌不尽
    2020-11-22 10:42

    While it seems @Miguel A. Arilla has pointed it out clearly and I voted up for him, I created on top of his useful solution another solution which looks neat but requires a lot more work.

    It definitely depends on the above solution. So basically I created something similar to Func> and I called it IServiceAccessor as an interface and then I had to add a some more extensions to the IServiceCollection as such:

    public static IServiceCollection AddSingleton(
                this IServiceCollection services,
                string instanceName
            )
                where TService : class
                where TImplementation : class, TService
                where TServiceAccessor : class, IServiceAccessor
            {
                services.AddSingleton();
                services.AddSingleton();
                var provider = services.BuildServiceProvider();
                var implementationInstance = provider.GetServices().Last();
                var accessor = provider.GetServices().First();
    
                var serviceDescriptors = services.Where(d => d.ServiceType == typeof(TServiceAccessor));
                while (serviceDescriptors.Any())
                {
                    services.Remove(serviceDescriptors.First());
                }
    
                accessor.SetService(implementationInstance, instanceName);
                services.AddSingleton(prvd => accessor);
                return services;
            }
    

    The service Accessor looks like:

     public interface IServiceAccessor
        {
             void Register(TService service,string name);
             TService Resolve(string name);
    
        }
    

    The end result,you will be able to register services with names or named instances like we used to do with other containers..for instance:

        services.AddSingleton("Symmetric");
        services.AddSingleton("Asymmetric");
    

    That is enough for now, but to make your work complete, it is better to add more extension methods as you can to cover all types of registrations following the same approach.

    There was another post on stackoverflow, but I can not find it, where the poster has explained in details why this feature is not supported and how to work around it, basically similar to what @Miguel stated. It was nice post even though I do not agree with each point because I think there are situation where you really need named instances. I will post that link here once I find it again.

    As a matter of fact, you do not need to pass that Selector or Accessor:

    I am using the following code in my project and it worked well so far.

     /// 
        /// Adds the singleton.
        /// 
        /// The type of the t service.
        /// The type of the t implementation.
        /// The services.
        /// Name of the instance.
        /// IServiceCollection.
        public static IServiceCollection AddSingleton(
            this IServiceCollection services,
            string instanceName
        )
            where TService : class
            where TImplementation : class, TService
        {
            var provider = services.BuildServiceProvider();
            var implementationInstance = provider.GetServices().LastOrDefault();
            if (implementationInstance.IsNull())
            {
                services.AddSingleton();
                provider = services.BuildServiceProvider();
                implementationInstance = provider.GetServices().Single();
            }
            return services.RegisterInternal(instanceName, provider, implementationInstance);
        }
    
        private static IServiceCollection RegisterInternal(this IServiceCollection services,
            string instanceName, ServiceProvider provider, TService implementationInstance)
            where TService : class
        {
            var accessor = provider.GetServices>().LastOrDefault();
            if (accessor.IsNull())
            {
                services.AddSingleton>();
                provider = services.BuildServiceProvider();
                accessor = provider.GetServices>().Single();
            }
            else
            {
                var serviceDescriptors = services.Where(d => d.ServiceType == typeof(IServiceAccessor));
                while (serviceDescriptors.Any())
                {
                    services.Remove(serviceDescriptors.First());
                }
            }
            accessor.Register(implementationInstance, instanceName);
            services.AddSingleton(prvd => implementationInstance);
            services.AddSingleton>(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(
            this IServiceCollection services,
            TService implementationInstance,
            string instanceName) where TService : class
        {
            var provider = services.BuildServiceProvider();
            return RegisterInternal(services, instanceName, provider, implementationInstance);
        }
    
        /// 
        /// Registers an interface for a class
        /// 
        /// The type of the t interface.
        /// The services.
        /// IServiceCollection.
        public static IServiceCollection As(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;
        }
    

提交回复
热议问题