前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ASP.NET Core 一个接口多个实现的依赖注入与动态选择

ASP.NET Core 一个接口多个实现的依赖注入与动态选择

作者头像
Edi Wang
发布2019-07-15 11:43:14
3.9K0
发布2019-07-15 11:43:14
举报
文章被收录于专栏:汪宇杰博客

点击蓝字

关注我

ASP.NET Core 自带的依赖注入(DI)非常实用,但是当一个接口有多个实现的时候怎么操作呢?运行时能否根据配置选择其中一种实现呢?能不能不用反射呢?很多小伙伴都有这样的疑问。今天我带大家看看如何在ASP.NET Core里不依赖反射,根据配置文件,在运行时动态选择一个接口的具体实现。

首先,这个需求其实来自我自己的博客系统。我的图片存储有两套:Azure Blob和文件系统,因此我写了一个接口,用了2套实现。想要做到能随时切换云存储或本地文件系统来保存博客文章的配图。因为这套代码比较复杂,因此我用一个最简明的例子来演示这个小技巧。

接口定义与两套实现

首先,接口长这样:

public interface IHelloer

{

string SayHello();

}

有两个实现:

public class HelloerA : IHelloer

{

public string SayHello()

{

return $"Hello from {nameof(HelloerA)}";

}

}

public class HelloerB : IHelloer

{

public string SayHello()

{

return $"Hello from {nameof(HelloerB)}";

}

}

注册依赖注入

和注册常规的ASP.NET Core DI完全一样,我们把HelloerAHelloerB都注册进去:

services.AddTransient<IHelloer, HelloerA>();

services.AddTransient<IHelloer, HelloerB>();

构造函数注入

刚才我们注册了同一接口的两套实现,那么ASP.NET Core运行时候会选择哪个实现呢?好奇可以试试:

public IHelloer Hello { get; set; }

public HomeController(IHelloer hello)

{

Hello = hello;

}

把结果输出到浏览器:

public IActionResult Index()

{

var message = Hello.SayHello();

return Content(message);

}

发现出来的是HelloB,就是我们注册DI时候顺序在最后的那个实现。

那么问题来了,我想要运行时选择HelloA怎么办?请往下看。

构造函数居然还能这样注入

其实,在ASP.NET Core中,当你对一个接口注册了多个实现的时候,构造函数是可以注入一个该接口集合的,这个集合里是所有注册过的实现。

private IEnumerable<IHelloer> Helloers { get; set; }

public HomeController(IEnumerable<IHelloer> helloers)

{

Helloers = helloers;

}

这样子出来的就是HelloA了:

public IActionResult Index()

{

var message = Helloers.First().SayHello();

return Content(message);

}

当然,在项目里,我们绝对是不能这样写死代码的,我们希望通过配置文件来选择具体实现。请往继续往下看。

配置文件

在appsettings.json里加入新配置:

"AppSettings": {

"CurrentHelloer": "HelloerA"

}

并且建立一个对应的class

public class AppSettings

{

public string CurrentHelloer { get; set; }

}

同样注册DI及构造器注入

services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));

private AppSettings AppSettings { get; set; }

private IEnumerable<IHelloer> Helloers { get; set; }

public HomeController(IEnumerable<IHelloer> helloers, IOptions<AppSettings> settings)

{

Helloers = helloers;

AppSettings = settings.Value;

}

这样一来,就可以用LINQ非常方便的根据配置文件,从接口集合中选择对应名字的实现:

public IActionResult Index()

{

var helloer = Helloers.FirstOrDefault(h => h.GetType().Name == AppSettings.CurrentHelloer);

var message = helloer?.SayHello();

return Content(message);

}

现在根据配置文件运行出来的就是HelloA了:

但是这个代码里竟然用了非政治正确的所谓影响性能的反射。为了避免代码被人鄙视,我们可以用一个workaround脱离反射。

不依赖反射

刚才我们用反射无非就是为了在运行时获得具体实现的class的名字。所以其实只要自己给每个class都加上一个名字属性就行了。并且我们可以利用nameof()来增加代码可维护性。

修改接口:

public interface IHelloer

{

string CurrentName { get; }

string SayHello();

}

修改实现:

public class HelloerA : IHelloer

{

public string CurrentName => nameof(HelloerA);

public string SayHello()

{

return $"Hello from {nameof(HelloerA)}";

}

}

public class HelloerB : IHelloer

{

public string CurrentName => nameof(HelloerB);

public string SayHello()

{

return $"Hello from {nameof(HelloerB)}";

}

}

修改LINQ:

public IActionResult Index()

{

var helloer = Helloers.FirstOrDefault(h => h.CurrentName == AppSettings.CurrentHelloer);

var message = helloer?.SayHello();

return Content(message);

}

完美运行:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-12-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 汪宇杰博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档