netcore中自带了Ioc框架,这也影响了我们的编码习惯,以前都是静态类或者直接new对象,现在有了Ioc框架的支持,我们也不必守旧,应当使用起来,接受这种对象管理方式。使用过java的同仁,都习惯了Spring,感觉离开了Spring就好像失去了灵魂一样。Spring经过多年的沉淀,非常的稳定和灵活,相比之下,netcore中自带的Ioc框架太过轻量,中规中矩的使用还算够用,但是其灵活性的确有待加强。比如属性注入、字段注入、方法参数注入等等,虽然说官方强烈建议使用构造函数注入,并且只提供了构造函数注入,但是我感觉后期肯定会支持更多都注入方法的,并且会结合Attribute提供更为灵活的功能,期望这一天能快点到来!

今天主要讨论其中一个问题,就是如何在netcore中注入同一个接口的多个实现,注册多个实现并不难,关键是如何优雅的取出来。下面我们用代码来说话:

  1. public interface IServiceA
  2. {
  3. string GetStr();
  4. }
  5. public class ImplA1 : IServiceA
  6. {
  7. public string GetStr()
  8. {
  9. return "ImplA1";
  10. }
  11. }
  12. public class ImplA2 : IServiceA
  13. {
  14. public string GetStr()
  15. {
  16. return "ImplA2";
  17. }
  18. }

上面的代码是有一个服务接口IServieA,和两个不同的实现类。如果我们这样注册:

  1. services.AddSingleton<IServiceA>(new ImplA1());
  2. services.AddSingleton<IServiceA>(new ImplA2());

第一种使用方式:

  1. public class HomeController : Controller
  2. {
  3. private readonly IServiceA serviceA;
  4. public HomeController(IServiceA serviceA)
  5. {
  6. this.serviceA = serviceA; //这里注入进来的是最后一个实现,也就是ImplA2
  7. }
  8. public IActionResult Index()
  9. {
  10. return Content(serviceA.GetStr());
  11. }
  12. }

这种方法肯定是不行的,因为只能获取注册的最后一个实现。

第二种使用方式:

  1. public class HomeController : Controller
  2. {
  3. private readonly IServiceA serviceA;
  4. public HomeController(IEnumerable<IServiceA> serviceAList)
  5. {
  6. this.serviceA = serviceAList.First(); //这里注入进来的是一个服务集合
  7. }
  8. public IActionResult Index()
  9. {
  10. return Content(serviceA.GetStr());
  11. }
  12. }

这种方法也不好,需要自己在集合中筛选要想的实现。

其实我们更希望是这样的,注册的时候可以使用一个标识,然后使用Attribute指定标识来获取具体的实现:

  1. services.AddSingleton<IServiceA>("impla1",new ImplA1()); //impla1为该实现在该接口的唯一标识
  2. services.AddSingleton<IServiceA>("impla2",new ImplA2()); //同上

可能会有这么一个Attribute类:

  1. public class IdentifierAttribute : Attribute
  2. {
  3. public IdentifierAttribute(string id)
  4. {
  5. this.Id = id;
  6. }
  7. public string Id { get; set; }
  8. }

然后使用的时候大概是这样:

  1. public HomeController([Identifier("impla1")]IServiceA serviceA)
  2. {
  3. this.serviceA = serviceA; //根据Identifier的指定,这里应该是ImplA1,
  4. }

如果能像Spring中@Autowired注解,注入字段就更好了:

  1. [Autowired]
  2. [Identifier("impla1")]
  3. private readonly IServiceA serviceA;

然而,我们该醒醒了,netcore中自带的Ioc没有提供类似这样的功能,我找了很久也没找到。

作为程序员,我们都要具备一种特质,那就是爱思考,经过思考,我感觉可以使用工厂来解决这个问题。这里只是提供一种思路和实现,有更好的方法大家可以留言讨论。
首先我们定义一个用于存储单例的集合类:

  1. public class SingletonFactory
  2. {
  3. Dictionary<Type, Dictionary<string, object>> serviceDict;
  4. public SingletonFactory()
  5. {
  6. serviceDict = new Dictionary<Type, Dictionary<string, object>>();
  7. }
  8. public TService GetService<TService>(string id) where TService : class
  9. {
  10. var serviceType = typeof(TService);
  11. return GetService<TService>(serviceType, id);
  12. }
  13. public TService GetService<TService>(Type serviceType, string id) where TService : class
  14. {
  15. if (serviceDict.TryGetValue(serviceType, out Dictionary<string, object> implDict))
  16. {
  17. if (implDict.TryGetValue(id, out object service))
  18. {
  19. return service as TService;
  20. }
  21. }
  22. return null;
  23. }
  24. public void AddService<TService>(TService service, string id) where TService : class
  25. {
  26. AddService(typeof(TService), service, id);
  27. }
  28. public void AddService(Type serviceType, object service, string id)
  29. {
  30. if (service != null)
  31. {
  32. if (serviceDict.TryGetValue(serviceType, out Dictionary<string, object> implDict))
  33. {
  34. implDict[id] = service;
  35. }
  36. else
  37. {
  38. implDict = new Dictionary<string, object>();
  39. implDict[id] = service;
  40. serviceDict[serviceType] = implDict;
  41. }
  42. }
  43. }
  44. }

这个类中使用一个嵌套的字典来存储服务类的实现,提供添加服务和获取服务的方法。

有了这样一个类,在netcore自带的Ioc中我们就可以获取更多的控制权了。且看下面代码。

注册服务:

  1. SingletonFactory singletonFactory = new SingletonFactory();
  2. singletonFactory.AddService<IServiceA>(new ImplA1(), "impla1");
  3. singletonFactory.AddService<IServiceA>(new ImplA2(), "impla2");
  4. services.AddSingleton(singletonFactory);

我们把同一个接口的多个实现都加到SingletonFactory中,然后把SingletonFactory注册到Ioc。

使用服务:

  1. private readonly IServiceA serviceA;
  2. public HomeController(SingletonFactory singletonFactory)
  3. {
  4. this.serviceA = singletonFactory.GetService<IServiceA>("impla2"); //使用标识从SingletonFactory获取自己想要的服务实现
  5. }

使用服务的时候我们注入SingletonFactory,然后从SingletonFactory根据标识获取具体的服务实现。

这个方法还算优雅吗?

在netcore中如何注入同一个接口的多个实现的更多相关文章

  1. SpringBoot JPA 中无法注入 JpaRepository 接口的问题及解决方案

    错误: 在Springboot  框架中使用JPA的过程中,怎么来实现数据库操作底层的交互呢?Spring JPA其实已经提供了一套很全面的解决方案,实现对数据库的增.删.查.改只需要继承JPA实现类 ...

  2. Spring Boot + JPA 多模块项目无法注入 JpaRepository 接口

    问题描述 Spring Boot + JPA 多模块项目,启动报异常: nested exception is org.springframework.beans.factory.NoSuchBean ...

  3. .netcore中的依赖注入

    IOC.DI相关概念的理解 1.依赖:简单的讲就是"引用到".例如AccountController.cs引用到IAccountService.cs,那么AccountContro ...

  4. .NetCore中三种注入方式的思考

    该篇内容由个人博客点击跳转同步更新!转载请注明出处! .NetCore彻底诠释了"万物皆可注入"这句话的含义,在.NetCore中到处可见注入的使用.因此core中也提供了三种注入 ...

  5. 由一个RABBITMQ监听器死循环引出的SPRING中BEAN和MAPPER接口的注入问题

    1 @Slf4j 2 @RestController 3 @Component 4 public class VouchersReceiverController implements Message ...

  6. Consul+Ocelot+Polly在.NetCore中使用(.NET5)-Ocelot+Polly缓存、限流、熔断、降级

    相关文章 Consul+Ocelot+Polly在.NetCore中使用(.NET5)-Consul服务注册,服务发现 Consul+Ocelot+Polly在.NetCore中使用(.NET5)-网 ...

  7. .NetCore中的日志(2)集成第三方日志工具

    .NetCore中的日志(2)集成第三方日志工具 0x00 在.NetCore的Logging组件中集成NLog 上一篇讨论了.NetCore中日志框架的结构,这一篇讨论一下.NetCore的Logg ...

  8. .NetCore中的日志(1)日志组件解析

    .NetCore中的日志(1)日志组件解析 0x00 问题的产生 日志记录功能在开发中很常用,可以记录程序运行的细节,也可以记录用户的行为.在之前开发时我一般都是用自己写的小工具来记录日志,输出目标包 ...

  9. spring容器注入一个接口的两个实现类

    spring容器中能拥有两个同种类型的bean吗?我有两个dao类同时实现一个接口,这两个接口注入时报了异常如下. org.springframework.beans.factory.NoSuchBe ...

随机推荐

  1. loadrunner函数解密之web_reg_find

    loadrunner工具的使用,最关键的在于3个地方: A:脚本的编写 B:场景设计 C:性能测试结果分析 其中难度比较大的第一步是:编写脚本,有很多人对于loadrunner里面的各种函数使用的并不 ...

  2. Delphi基础必记-快捷键

    快捷键: F12 代码窗口/窗体之间切换Ctrl + Shift + F 查找文件 Ctrl + Shift + G 为接口加入新的GUIDF4 运行到光标位置 F5 设置/取消断点 或用光标点击F7 ...

  3. JS结合a标签的使用

    a标签可以当作按钮使用,也可以当作连接. <a href=javascript:test(5)>弹出5</a>    会直接调用JS函数(注意中间没引号) <a href ...

  4. float/double 浮点数据*100精度丢失问题

    工作中微信支付碰到的一个问题,金额是float数字,微信参数需要分且必须是整数,所以*100的时候就有问题了 System.out.println(9.9f*100); //989.99994Syst ...

  5. Dubbo配置参数的优先级

    总结为: 1).Java运行时虚拟机参数 eg:-Ddubbo.protocol.port=20880 2).dubbo.xml || application.properties(SpringBoo ...

  6. Linux下查看文件或文件夹大小的命令df 、du、ls

    转自:http://www.cnblogs.com/benio/archive/2010/10/13/1849946.html 当磁盘大小超过标准时会有报警提示,这时如果掌握df和du命令是非常明智的 ...

  7. centos6.5下java和tomcat环境部署

    软件包: tomcat安装包 apache-tomcat-7.0.67.zip jdk安装包 jdk-6u10-linux-x64.bin 下载地址:http://www.oracle.com/tec ...

  8. S5PV210 LCD显示

    第一节 S5PV210 LCD控制器要使一块LCD正常显示文字或图像,不仅需要LCD驱动器,还需要相应的LCD控制器.LCD控制器的主要作用是将在系统存储器中的显示缓冲区中的LCD图像数据传送到外部L ...

  9. Android动画分类

    动画分类 View动画(补间动画).帧动画.属性动画 View动画(补间动画)包括:平移.旋转.缩放.透明度,View动画是一种渐近式动画 帧动画:图片切换动画 属性动画:通过动态改变对象的属性达到动 ...

  10. eslint ":"号

    eslint规则默认是没有;号的,如果也没要加;号,那就要在.eslintrc.js里面,加配置: 'semi':['error',always']   强制有;号,没有就报错 参考地址:http:/ ...