代码之家  ›  专栏  ›  技术社区  ›  Erçin Dedeoğlu

.NET Core通过addTransient实现多个IDBConnection[重复]

  •  1
  • Erçin Dedeoğlu  · 技术社区  · 6 年前

    我有来自同一接口的服务

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

    通常其他ioc容器如 Unity 允许您注册一些 Key 这使他们与众不同。

    在ASP.NET Core中,如何注册这些服务并在运行时基于某个键进行解析?

    我没有看到任何 Add 服务方法采用 key name 通常用于区分具体实现的参数。

        public void ConfigureServices(IServiceCollection services)
        {            
             // How do I register services here of the same interface            
        }
    
    
        public MyController:Controller
        {
           public void DoSomeThing(string key)
           { 
              // How do get service based on key
           }
        }
    

    工厂模式是这里唯一的选择吗?

    更新1
    我已经看过那篇文章了 here 这说明了当我们有多个concreate实现时,如何使用工厂模式来获取服务实例。然而,这仍然不是完全的解决办法。当我打电话 _serviceProvider.GetService() 方法I无法将数据注入构造函数。例如考虑这个例子

    public class ServiceA : IService
    {
         private string _efConnectionString;
         ServiceA(string efconnectionString)
         {
           _efConnecttionString = efConnectionString;
         } 
    }
    
    public class ServiceB : IService
    {    
       private string _mongoConnectionString;
       public ServiceB(string mongoConnectionString)
       {
          _mongoConnectionString = mongoConnectionString;
       }
    }
    
    public class ServiceC : IService
    {    
        private string _someOtherConnectionString
        public ServiceC(string someOtherConnectionString)
        {
          _someOtherConnectionString = someOtherConnectionString;
        }
    }
    

    怎么能 _ serviceprovider.getService() 插入适当的连接字符串? 在Unity或任何其他国际奥委会中,我们都可以在类型注册时这样做。我可以用 IOption 但是,这将需要我注入所有设置,我无法将特定的connectionstring注入服务。

    还要注意,我试图避免使用其他容器(包括unity),因为这样我就必须将所有其他容器(例如控制器)也注册到新容器中。

    另外,使用工厂模式创建服务实例也不利于DIP,因为工厂增加了客户机被迫依赖的依赖项的数量 details here

    所以我认为asp.net内核中的默认di缺少两件事
    1>使用键注册实例
    2>在注册期间将静态数据注入构造函数

    0 回复  |  直到 5 年前
        1
  •  138
  •   Miguel A. Arilla    6 年前

    我用 Func 当我发现自己在这种情况下。

    services.AddTransient<Consumer>();
    
    services.AddTransient<ServiceA>();
    services.AddTransient<ServiceB>();
    services.AddTransient<ServiceC>();
    
    services.AddTransient<Func<string, IService>>(serviceProvider => key =>
    {
        switch(key)
        {
            case "A":
                return serviceProvider.GetService<ServiceA>();
            case "B":
                return serviceProvider.GetService<ServiceB>();
            case "C":
                return serviceProvider.GetService<ServiceC>();
            default:
                throw new KeyNotFoundException(); // or maybe return null, up to you
        }
    });
    

    并从任何在DI注册的类中使用,例如:

    public class Consumer
    {
        private readonly Func<string, IService> serviceAccessor;
    
        public Consumer(Func<string, IService> serviceAccessor)
        {
            this.serviceAccessor = serviceAccesor;
        }
    
        public void UseServiceA()
        {
            //use serviceAccessor field to resolve desired type
            serviceAccessor("A").DoIServiceOperation();
        }
    }
    

    更新

    请记住,在这个例子中,解析的关键是一个字符串,为了简单起见,因为op特别要求这个例子。

    但是,您可以使用任何自定义的解析类型作为键,因为您通常不希望大型n-case开关损坏您的代码。取决于你的应用程序的扩展方式。

        2
  •  58
  •   rnrneverdies    6 年前

    另一种选择是使用扩展方法 GetServices Microsoft.Extensions.DependencyInjection

    将您的服务注册为:

    services.AddSingleton<IService, ServiceA>();
    services.AddSingleton<IService, ServiceB>();
    services.AddSingleton<IService, ServiceC>();
    

    然后用一点林肯的话解决:

    var services = serviceProvider.GetServices<IService>();
    var serviceB = services.First(o => o.GetType() == typeof(ServiceB));
    

    var serviceZ = services.First(o => o.Name.Equals("Z"));
    

    (假设 IService 有一个名为“name”的字符串属性

    一定要有 using Microsoft.Extensions.DependencyInjection;

    更新

    ASPNET 2.1来源: GetServices

        3
  •  15
  •   Gerardo Grignoli    8 年前

    它不受 Microsoft.Extensions.DependencyInjection .

    但是您可以插入另一个依赖注入机制,比如 StructureMap See it's Home page 它是 GitHub Project .

    一点也不难:

    1. 将依赖项添加到 project.json :

      "Structuremap.Microsoft.DependencyInjection" : "1.0.1",
      
    2. 将其注入内部的asp.net管道 ConfigureServices 注册你的课程 (see docs)

      public IServiceProvider ConfigureServices(IServiceCollection services) // returns IServiceProvider !
      {
          // Add framework services.
          services.AddMvc();
          services.AddWhatever();
      
          //using StructureMap;
          var container = new Container();
          container.Configure(config =>
          {
              // Register stuff in container, using the StructureMap APIs...
              config.For<IPet>().Add(new Cat("CatA")).Named("A");
              config.For<IPet>().Add(new Cat("CatB")).Named("B");
              config.For<IPet>().Use("A"); // Optionally set a default
              config.Populate(services);
          });
      
          return container.GetInstance<IServiceProvider>();
      }
      
    3. 然后,要获取命名实例,您需要请求 IContainer

      public class HomeController : Controller
      {
          public HomeController(IContainer injectedContainer)
          {
              var myPet = injectedContainer.GetInstance<IPet>("B");
              string name = myPet.Name; // Returns "CatB"
      

    就这样。

    对于要构建的示例,您需要

        public interface IPet
        {
            string Name { get; set; }
        }
    
        public class Cat : IPet
        {
            public Cat(string name)
            {
                Name = name;
            }
    
            public string Name {get; set; }
        }
    
        4
  •  11
  •   neleus    7 年前

    我也遇到过同样的问题,我想和大家分享我是如何解决这个问题的。

    正如你提到的,有两个问题。 第一个:

    在ASP.NET Core中,如何注册这些服务并在 基于某个密钥的运行时?

    我们有什么选择?人们建议两个:

    • 使用自定义工厂(如 _myFactory.GetServiceByKey(key) )

    • 使用另一个DI引擎(如 _unityContainer.Resolve<IService>(key) )

    工厂模式是这里唯一的选择吗?

    事实上,这两个选项都是工厂,因为每个ioc容器也是一个工厂(但高度可配置且复杂)。在我看来,其他的选择也是工厂模式的变化。

    那么,还有什么更好的选择呢?这里我同意@sock建议使用定制工厂,这就是原因。

    首先,当不需要新的依赖项时,我总是尽量避免添加它们。所以我同意你的观点。此外,使用两个di框架比创建自定义工厂抽象更糟糕。在第二种情况下,您必须添加新的包依赖项(如unity),但依赖于新的工厂接口在这里并不那么糟糕。我相信,asp.net core di的主要思想是简单。它维护了一组最少的特性 KISS principle . 如果你需要一些额外的功能,那么自己动手或使用相应的 Plungin 实现所需功能(开闭原理)的。

    其次,通常我们需要为单个服务注入许多命名依赖项。在统一的情况下,您可能需要为构造函数参数指定名称(使用 InjectionConstructor )。此注册使用反射和 一些聪明的逻辑 猜测构造函数的参数。如果注册与构造函数参数不匹配,这也可能导致运行时错误。另一方面,当使用自己的工厂时,您可以完全控制如何提供构造函数参数。它更具可读性,并且在编译时得到解决。 接吻原则 再一次。

    第二个问题:

    如何插入适当的连接 弦?

    首先,我同意你的看法 IOptions (因此在包装上 Microsoft.Extensions.Options.ConfigurationExtensions )不是个好主意。我看到有人在讨论 眼动 对它的好处有不同的看法。同样,当不需要新的依赖项时,我尽量避免添加它们。真的需要吗?我认为不是。否则每个实现都必须依赖它,而不需要来自该实现的任何明确需求(对我来说,这看起来像是违反了isp,我也同意你的看法)。这也适用于依赖工厂的情况,但在这种情况下 可以 避免。

    为此,asp.net core di提供了一个非常好的重载:

    var mongoConnection = //...
    var efConnection = //...
    var otherConnection = //...
    services.AddTransient<IMyFactory>(
                 s => new MyFactoryImpl(
                     mongoConnection, efConnection, otherConnection, 
                     s.GetService<ISomeDependency1>(), s.GetService<ISomeDependency2>())));
    
        5
  •  10
  •   Sock    8 年前

    没错,内置的asp.net核心容器没有注册多个服务然后检索特定服务的概念,正如您所建议的,在这种情况下,工厂是唯一真正的解决方案。

    或者,您可以切换到第三方容器,如Unity或StructureMap,它确实提供了您需要的解决方案(在这里记录: https://docs.asp.net/en/latest/fundamentals/dependency-injection.html?#replacing-the-default-services-container )

        6
  •  6
  •   T Brown    6 年前

    我只是简单地注射了

    在startup.cs中配置服务

    Assembly.GetEntryAssembly().GetTypesAssignableFrom<IService>().ForEach((t)=>
                    {
                        services.AddScoped(typeof(IService), t);
                    });
    

    服务文件夹

    public interface IService
    {
        string Name { get; set; }
    }
    
    public class ServiceA : IService
    {
        public string Name { get { return "A"; } }
    }
    
    public class ServiceB : IService
    {    
        public string Name { get { return "B"; } }
    }
    
    public class ServiceC : IService
    {    
        public string Name { get { return "C"; } }
    }
    

    Mycontroller.cs公司

    public class MyController
    {
        private readonly IEnumerable<IService> _services;
        public MyController(IEnumerable<IService> services)
        {
            _services = services;
        }
        public void DoSomething()
        {
            var service = _services.Where(s => s.Name == "A").Single();
        }
    ...
    }
    

    扩展.cs

        public static List<Type> GetTypesAssignableFrom<T>(this Assembly assembly)
        {
            return assembly.GetTypesAssignableFrom(typeof(T));
        }
        public static List<Type> GetTypesAssignableFrom(this Assembly assembly, Type compareType)
        {
            List<Type> ret = new List<Type>();
            foreach (var type in assembly.DefinedTypes)
            {
                if (compareType.IsAssignableFrom(type) && compareType != type)
                {
                    ret.Add(type);
                }
            }
            return ret;
        }
    
        7
  •  4
  •   ArcadeRenegade    7 年前

    显然,你可以注入你的服务接口的IEnumerable! 然后使用linq找到您想要的实例。

    我的示例是aws sns服务,但是您可以对任何注入的服务执行同样的操作。

    启动

    foreach (string snsRegion in Configuration["SNSRegions"].Split(',', StringSplitOptions.RemoveEmptyEntries))
    {
        services.AddAWSService<IAmazonSimpleNotificationService>(
            string.IsNullOrEmpty(snsRegion) ? null :
            new AWSOptions()
            {
                Region = RegionEndpoint.GetBySystemName(snsRegion)
            }
        );
    }
    
    services.AddSingleton<ISNSFactory, SNSFactory>();
    
    services.Configure<SNSConfig>(Configuration);
    

    snsconfig公司

    public class SNSConfig
    {
        public string SNSDefaultRegion { get; set; }
        public string SNSSMSRegion { get; set; }
    }
    

    appsettings.json设置

      "SNSRegions": "ap-south-1,us-west-2",
      "SNSDefaultRegion": "ap-south-1",
      "SNSSMSRegion": "us-west-2",
    

    SNS工厂

    public class SNSFactory : ISNSFactory
    {
        private readonly SNSConfig _snsConfig;
        private readonly IEnumerable<IAmazonSimpleNotificationService> _snsServices;
    
        public SNSFactory(
            IOptions<SNSConfig> snsConfig,
            IEnumerable<IAmazonSimpleNotificationService> snsServices
            )
        {
            _snsConfig = snsConfig.Value;
            _snsServices = snsServices;
        }
    
        public IAmazonSimpleNotificationService ForDefault()
        {
            return GetSNS(_snsConfig.SNSDefaultRegion);
        }
    
        public IAmazonSimpleNotificationService ForSMS()
        {
            return GetSNS(_snsConfig.SNSSMSRegion);
        }
    
        private IAmazonSimpleNotificationService GetSNS(string region)
        {
            return GetSNS(RegionEndpoint.GetBySystemName(region));
        }
    
        private IAmazonSimpleNotificationService GetSNS(RegionEndpoint region)
        {
            IAmazonSimpleNotificationService service = _snsServices.FirstOrDefault(sns => sns.Config.RegionEndpoint == region);
    
            if (service == null)
            {
                throw new Exception($"No SNS service registered for region: {region}");
            }
    
            return service;
        }
    }
    
    public interface ISNSFactory
    {
        IAmazonSimpleNotificationService ForDefault();
    
        IAmazonSimpleNotificationService ForSMS();
    }
    

    现在,您可以在自定义服务或控制器中获取所需区域的sns服务

    public class SmsSender : ISmsSender
    {
        private readonly IAmazonSimpleNotificationService _sns;
    
        public SmsSender(ISNSFactory snsFactory)
        {
            _sns = snsFactory.ForSMS();
        }
    
        .......
     }
    
    public class DeviceController : Controller
    {
        private readonly IAmazonSimpleNotificationService _sns;
    
        public DeviceController(ISNSFactory snsFactory)
        {
            _sns = snsFactory.ForDefault();
        }
    
         .........
    }
    
        8
  •  2
  •   Assil    6 年前

    虽然看起来“米格尔A.阿里拉已经清楚地指出了这一点,我投了他的票,但我在他有用的解决方案的基础上创造了另一个解决方案,看起来很整洁,但需要做更多的工作。

    这完全取决于上述解决方案。所以基本上我创造了类似于 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;
            }
    

    服务访问器如下所示:

     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;
        }
    
        9
  •  2
  •   Gray    6 年前

    这次聚会有点晚了,但我的解决办法是:…

    startup.cs或program.cs如果是通用处理程序…

    services.AddTransient<IMyInterface<CustomerSavedConsumer>, CustomerSavedConsumer>();
    services.AddTransient<IMyInterface<ManagerSavedConsumer>, ManagerSavedConsumer>();
    

    T接口设置界面

    public interface IMyInterface<T> where T : class, IMyInterface<T>
    {
        Task Consume();
    }
    

    t接口的具体实现

    public class CustomerSavedConsumer: IMyInterface<CustomerSavedConsumer>
    {
        public async Task Consume();
    }
    
    public class ManagerSavedConsumer: IMyInterface<ManagerSavedConsumer>
    {
        public async Task Consume();
    }
    

    希望如果这样做有什么问题,有人会善意地指出为什么这样做是错误的。

        10
  •  1
  •   jlc397    7 年前

    工厂的方法当然是可行的。另一种方法是使用继承创建从iSeries设备继承的单个接口,在iSeries设备实现中实现继承的接口,并注册继承的接口而不是基接口。添加继承层次结构还是工厂是“正确的”模式都取决于您与谁交谈。在处理同一应用程序中使用泛型的多个数据库提供程序时,我经常必须使用此模式,例如 IRepository<T> 作为数据访问的基础。

    接口和实现示例:

    public interface IService 
    {
    }
    
    public interface IServiceA: IService
    {}
    
    public interface IServiceB: IService
    {}
    
    public IServiceC: IService
    {}
    
    public class ServiceA: IServiceA 
    {}
    
    public class ServiceB: IServiceB
    {}
    
    public class ServiceC: IServiceC
    {}
    

    容器:

    container.Register<IServiceA, ServiceA>();
    container.Register<IServiceB, ServiceB>();
    container.Register<IServiceC, ServiceC>();
    
        11
  •  1
  •   littgle    6 年前

    我的解决方案是什么值得…考虑转投温莎城堡,因为我不喜欢上面的任何解决方案。对不起的!!

    public interface IStage<out T> : IStage { }
    
    public interface IStage {
          void DoSomething();
    }
    

    创建各种实现

    public class YourClassA : IStage<YouClassA> { 
        public void DoSomething() 
        {
            ...TODO
        }
    }
    
    public class YourClassB : IStage<YourClassB> { .....etc. }
    

    登记处

    services.AddTransient<IStage<YourClassA>, YourClassA>()
    services.AddTransient<IStage<YourClassB>, YourClassB>()
    

    构造函数和实例用法…

    public class Whatever
    {
       private IStage ClassA { get; }
    
       public Whatever(IStage<YourClassA> yourClassA)
       {
             ClassA = yourClassA;
       }
    
       public void SomeWhateverMethod()
       {
            ClassA.DoSomething();
            .....
       }
    
        12
  •  1
  •   Stefan Steiger Marco van de Voort    5 年前

    巫术。
    我认为这里的人们正在重新发明轮子——如果我可以这么说的话,那就糟了……
    如果要按键注册组件,请使用字典:

    System.Collections.Generic.Dictionary<string, IConnectionFactory> dict = 
        new System.Collections.Generic.Dictionary<string, IConnectionFactory>(
            System.StringComparer.OrdinalIgnoreCase);
    
    dict.Add("ReadDB", new ConnectionFactory("connectionString1"));
    dict.Add("WriteDB", new ConnectionFactory("connectionString2"));
    dict.Add("TestDB", new ConnectionFactory("connectionString3"));
    dict.Add("Analytics", new ConnectionFactory("connectionString4"));
    dict.Add("LogDB", new ConnectionFactory("connectionString5"));
    

    然后向服务集合注册字典:

    services.AddSingleton<System.Collections.Generic.Dictionary<string, IConnectionFactory>>(dict);
    

    如果您不愿意获取字典并按键访问它,则可以通过向服务集合添加其他键查找方法来隐藏字典:
    (使用delegate/closure应该可以让潜在的维护者有机会了解正在发生的事情-箭头符号有点晦涩)

    services.AddTransient<Func<string, IConnectionFactory>>(
        delegate (IServiceProvider sp)
        {
            return
                delegate (string key)
                {
                    System.Collections.Generic.Dictionary<string, IConnectionFactory> dbs = Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService
     <System.Collections.Generic.Dictionary<string, IConnectionFactory>>(sp);
    
                    if (dbs.ContainsKey(key))
                        return dbs[key];
    
                    throw new System.Collections.Generic.KeyNotFoundException(key); // or maybe return null, up to you
                };
        });
    

    现在您可以使用

    IConnectionFactory logDB = Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<Func<string, IConnectionFactory>>(serviceProvider)("LogDB");
    logDB.Connection
    

    System.Collections.Generic.Dictionary<string, IConnectionFactory> dbs = Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<System.Collections.Generic.Dictionary<string, IConnectionFactory>>(serviceProvider);
    dbs["logDB"].Connection
    

    正如我们所看到的,第一个完全是多余的,因为你也可以用字典来完成,而不需要闭包和addtransient(如果你使用vb,连大括号都不一样):

    IConnectionFactory logDB = Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<System.Collections.Generic.Dictionary<string, IConnectionFactory>>(serviceProvider)["logDB"];
    logDB.Connection
    

    (更简单更好-但您可能希望将其用作扩展方法)

    当然,如果你不喜欢字典,你也可以在你的界面上设置一个属性 Name (或其他),然后按键查找:

    services.AddSingleton<IConnectionFactory>(new ConnectionFactory("ReadDB"));
    services.AddSingleton<IConnectionFactory>(new ConnectionFactory("WriteDB"));
    services.AddSingleton<IConnectionFactory>(new ConnectionFactory("TestDB"));
    services.AddSingleton<IConnectionFactory>(new ConnectionFactory("Analytics"));
    services.AddSingleton<IConnectionFactory>(new ConnectionFactory("LogDB"));
    
    
    
    // https://stackoverflow.com/questions/39174989/how-to-register-multiple-implementations-of-the-same-interface-in-asp-net-core
    services.AddTransient<Func<string, IConnectionFactory>>(
        delegate(IServiceProvider sp)
        {
            return
                delegate(string key)
                {
                    System.Collections.Generic.IEnumerable<IConnectionFactory> svs = 
                        sp.GetServices<IConnectionFactory>();
    
                    foreach (IConnectionFactory thisService in svs)
                    {
                        if (key.Equals(thisService.Name, StringComparison.OrdinalIgnoreCase))
                            return thisService;
                    }
    
                    return null;
                };
        });
    

    但这需要更改接口以适应该属性,并且通过许多元素的循环应该比关联数组查找(dictionary)慢得多。
    不过,很高兴知道,这件事可以不带任何偏见。

    这些只是我的0.05美元

        13
  •  0
  •   Andrew Stakhov    6 年前

    虽然开箱即用的实现不提供它,但这里有一个示例项目,它允许您注册命名实例,然后将inamedservicefactory注入到代码中,并按名称拉出实例。与这里的其他facury解决方案不同,它允许您注册 相同的实现 但配置不同

    https://github.com/macsux/DotNetDINamedInstances

        14
  •  0
  •   Craig Brunetti    6 年前

    服务换服务怎么样?

    如果我们有一个inamedservice接口(具有.name属性),我们可以为.getservice(string name)编写一个iservicecollection扩展,扩展将接受该string参数,并对其本身执行.getservices()操作,在每个返回的实例中,找到其inamedservice.name的实例匹配给定的名称。

    这样地:

    public interface INamedService
    {
        string Name { get; }
    }
    
    public static T GetService<T>(this IServiceProvider provider, string serviceName)
        where T : INamedService
    {
        var candidates = provider.GetServices<T>();
        return candidates.FirstOrDefault(s => s.Name == serviceName);
    }
    

    因此,您的imyservice必须实现inamedservice,但您将获得所需的基于密钥的解决方案,对吗?

    公平地说,甚至必须拥有这个inamedservice接口看起来也很难看,但是如果您想更进一步,使事情变得更优雅,那么在这个扩展中的代码可以找到实现/类上的[namedserviceattribute(“a”)],它也能正常工作。更公平地说,反射是缓慢的,因此优化可能是有序的,但老实说,这是di引擎应该帮助的。速度和简单性是TCO的两大贡献因素。

    总之,不需要显式工厂,因为“查找命名服务”是一个可重用的概念,工厂类不作为解决方案进行扩展。一个func<>看起来不错,但是一个switch block是这样的 布雷 ,同样,您将像编写工厂一样频繁地编写函数。从简单的、可重用的、更少的代码开始,如果结果不是为了你,那么就变得复杂。

        15
  •  0
  •   vrluckyin    5 年前

    扩展@rnrneverdies的解决方案。除了tostring(),还可以使用以下选项-1)公共属性实现,2)由@craig brunetti建议的服务服务。

    public interface IService { }
    public class ServiceA : IService
    {
        public override string ToString()
        {
            return "A";
        }
    }
    
    public class ServiceB : IService
    {
        public override string ToString()
        {
            return "B";
        }
    
    }
    
    /// <summary>
    /// extension method that compares with ToString value of an object and returns an object if found
    /// </summary>
    public static class ServiceProviderServiceExtensions
    {
        public static T GetService<T>(this IServiceProvider provider, string identifier)
        {
            var services = provider.GetServices<T>();
            var service = services.FirstOrDefault(o => o.ToString() == identifier);
            return service;
        }
    }
    
    public void ConfigureServices(IServiceCollection services)
    {
        //Initials configurations....
    
        services.AddSingleton<IService, ServiceA>();
        services.AddSingleton<IService, ServiceB>();
        services.AddSingleton<IService, ServiceC>();
    
        var sp = services.BuildServiceProvider();
        var a = sp.GetService<IService>("A"); //returns instance of ServiceA
        var b = sp.GetService<IService>("B"); //returns instance of ServiceB
    
        //Remaining configurations....
    }