ExpandoObject与DynamicObject的使用 RabbitMQ与.net core(一)安装 RabbitMQ与.net core(二)Producer与Exchange ASP.NET Core 2.1 : 十五.图解路由(2.1 or earler) .NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了
ExpandoObject与DynamicObject的使用
using ImpromptuInterface; using System; using System.Dynamic; namespace ConsoleApp2 { class Program { static void Main(string[] args) { dynamic expando = new ExpandoObject(); expando.name = "cys"; expando.Add = new Func<int, int>(i=> { return i; }); var expando_result = expando.Add(1); Console.WriteLine(expando.GetType().Name); dynamic dobject = new BB { name="aaa" }; var dobject_result = dobject.ADD(); dobject.Name = "cys"; Console.WriteLine(dobject.Name); Console.WriteLine(dobject.GetType().Name); } } public class BB : DynamicObject { public string name { get; set; } public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { result = "a"; return true; } public override bool TryGetMember(GetMemberBinder binder, out object result) { result = name; return true; } public override bool TrySetMember(SetMemberBinder binder, object value) { name = value.ToString(); return true; } } }
RabbitMQ与.net core(一)安装
QQ讨论群:953553560
正文
一、安装Erlang环境
前提:erlang版本与rabbitmq版本需按照要求对应!!!
1、在安装erlang之前先安装下依赖文件(这一步不要忘掉了, 不然后面./configure的时候要报错):
[root@iZwz9eailk2tci1wywk9p2Z local]# yum install gcc glibc-devel make ncurses-devel openssl-devel xmlto
2、到erlang官网去下载erlang安装包
官网地址:http://www.erlang.org/downloads
右键复制连接地址,用wget进行下载
[root@iZwz9eailk2tci1wywk9p2Z local]# wget -c http://erlang.org/download/otp_src_20.2.tar.gz
接下来解压:
[root@iZwz9eailk2tci1wywk9p2Z local]# tar -zxvf otp_src_20.2.tar.gz [root@iZwz9eailk2tci1wywk9p2Z local]# cd otp_src_20.2/
3、编译安装( 我这里指定编译安装后放在/usr/local/erlang目录里面,这个你们可以改成其他的 ):
[root@iZwz9eailk2tci1wywk9p2Z otp_src_20.2]# ./configure --prefix=/usr/local/erlang [root@iZwz9eailk2tci1wywk9p2Z otp_src_20.2]# make && make install
4、测试安装是否成功:
[root@iZwz9eailk2tci1wywk9p2Z erlang]# cd /usr/local/erlang/bin/ [root@iZwz9eailk2tci1wywk9p2Z bin]# ./erl
若出现以下界面,则说明我们erlang配置OK了
输入 halt(). 退出控制台, 注意,halt后面有个点哈
5、配置环境变量(ps:这个跟java的环境变量配置是差不多的)
[root@iZwz9eailk2tci1wywk9p2Z local]# vim /etc/profile
在末尾加入这么一行即可:
export PATH=$PATH:/usr/local/erlang/bin
更新配置文件:
[root@iZwz9eailk2tci1wywk9p2Z local]# source /etc/profile
更新之后在任意地方输入erl能进入命令行, 那么就说明配置成功了。
接下来进入我们的核心部分:配置rabbitmq
二、安装rabbitmq
1、到官网下载最新安装包:http://www.rabbitmq.com/releases/rabbitmq-server/
[root@iZwz9eailk2tci1wywk9p2Z local]# wget -c http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.15/rabbitmq-server-generic-unix-3.6.15.tar.xz
解压:
[root@iZwz9eailk2tci1wywk9p2Z local]# xz -d rabbitmq-server-generic-unix-3.6.15.tar.xz [root@iZwz9eailk2tci1wywk9p2Z local]# tar -xvf rabbitmq-server-generic-unix-3.6.15.tar
2、配置rabbitmq的环境变量(这个跟上面的erlang配置以及java的环境变量差不多)
[root@iZwz9eailk2tci1wywk9p2Z local]# vim /etc/profile
在末尾加入以下配置:
export PATH=$PATH:/usr/local/rabbitmq_server-3.6.15/sbin
更新配置文件:
[root@iZwz9eailk2tci1wywk9p2Z local]# source /etc/profile
3、rabbitmq的基本操作:
启动:rabbitmq-server -detached
关闭:rabbitmqctl stop
查看状态:rabbitmqctl status
4、配置rabbitmq网页管理插件
启用插件:
root@iZwz9eailk2tci1wywk9p2Z local]# rabbitmq-plugins enable rabbitmq_management
访问管理页面:http://192.168.?.?:15672 端口默认为15672
默认来宾用户:guest, 来宾用户密码:guest
5、开启rabbitmq远程访问
添加用户:rabbitmqctl add_user XRom XRom123 //XRom是用户名, XRom123是用户密码
添加权限:rabbitmqctl set_permissions -p "/" XRom ".*" ".*" ".*"
修改用户角色:rabbitmqctl set_user_tags XRom administrator
然后就可以远程访问了,然后可直接配置用户权限等信息
6、rabbitmq常用命令
add_user <UserName> <Password>
delete_user <UserName>
change_password <UserName> <NewPassword>
list_users
add_vhost <VHostPath>
delete_vhost <VHostPath>
list_vhostsset_permissions [-p <VHostPath>] <UserName> <Regexp> <Regexp> <Regexp>
clear_permissions [-p <VHostPath>] <UserName>
list_permissions [-p <VHostPath>]
list_user_permissions <UserName>
list_queues [-p <VHostPath>] [<QueueInfoItem> ...]
list_exchanges [-p <VHostPath>] [<ExchangeInfoItem> ...]
list_bindings [-p <VHostPath>]
list_connections [<ConnectionInfoItem> ...]
参考:
https://www.cnblogs.com/wyt007/p/9073258.html
https://www.cnblogs.com/xrog/p/8519967.html
RabbitMQ与.net core(二)Producer与Exchange
QQ讨论群:953553560
正文
Producer:消息的生产者,也就是创建消息的对象
Exchange:消息的接受者,也就是用来接收消息的对象,Exchange接收到消息后将消息按照规则发送到与他绑定的Queue中。下面我们来定义一个Producer与Exchange。
1.新建.netcore console项目,并引入RabbitMQ.Client的Nuget包
2.创建Exchange
using RabbitMQ.Client; namespace RabbitMQConsole { class Program { static void Main(string[] args) { ConnectionFactory factory = new ConnectionFactory(); factory.HostName = "39.**.**.**"; factory.Port = 5672; factory.VirtualHost = "/"; factory.UserName = "root"; factory.Password = "root"; var exchange = "change2"; var route = "route2"; var queue = "queue2"; using (var connection = factory.CreateConnection()) { using (var channel = connection.CreateModel()) { channel.ExchangeDeclare(exchange, type:"direct", durable: true, autoDelete: false); //创建Exchange } } } } }
可以看到Echange的参数有:
type:可选项为,fanout,direct,topic,headers。区别如下:
fanout:发送到所有与当前Exchange绑定的Queue中
direct:发送到与消息的routeKey相同的Rueue中
topic:fanout的模糊版本
headers:发送到与消息的header属性相同的Queue中
durable:持久化
autoDelete:当最后一个绑定(队列或者exchange)被unbind之后,该exchange自动被删除。
运行程序,可以在可视化界面看到change2
接下来我们可以创建与change2绑定的queue
3.创建Queue
using (var channel = connection.CreateModel()) { channel.ExchangeDeclare(exchange, type: "direct", durable: true, autoDelete: false); channel.QueueDeclare(queue, durable: true, exclusive: false, autoDelete: false); #创建queue2 channel.QueueBind(queue, exchange, route); #将queue2绑定到exchange2 }
可以看到Echange的参数有:
durable:持久化
exclusive:如果为true,则queue只在channel存在时存在,channel关闭则queue消失
autoDelete:当最后一个绑定(队列或者exchange)被unbind之后,该exchange自动被删除。
去可视化界面看Queue
4.发送消息
using (var channel = connection.CreateModel()) { channel.ExchangeDeclare(exchange, type: "direct", durable: true, autoDelete: false); channel.QueueDeclare(queue, durable: true, exclusive: false, autoDelete: false); channel.QueueBind(queue, exchange, route); var props = channel.CreateBasicProperties(); props.Persistent = true; #持久化 channel.BasicPublish(exchange, route, true, props, Encoding.UTF8.GetBytes("hello rabbit")); }
5.消费消息
using RabbitMQ.Client; using System; using System.Text; namespace RabbitMQClient { class Program { private static readonly ConnectionFactory rabbitMqFactory = new ConnectionFactory() { HostName = "39.**.**.**", Port = 5672, UserName = "root", Password = "root", VirtualHost = "/" }; static void Main(string[] args) { var exchange = "change2"; var route = "route2"; var queue = "queue2"; using (IConnection conn = rabbitMqFactory.CreateConnection()) using (IModel channel = conn.CreateModel()) { channel.ExchangeDeclare(exchange, "direct", durable: true, autoDelete: false); channel.QueueDeclare(queue, durable: true, exclusive: false, autoDelete: false); channel.QueueBind(queue, exchange, route); while (true) { var message = channel.BasicGet(queue, true); #第二个参数说明自动释放消息,如为false需手动释放消息 if(message!=null) { var msgBody = Encoding.UTF8.GetString(message.Body); Console.WriteLine(string.Format("***接收时间:{0},消息内容:{1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), msgBody)); } System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1)); } } } } }
运行查看结果
查看可视化界面
6.手动释放消息
while (true) { var message = channel.BasicGet(queue, false);#设置为手动释放 if(message!=null) { var msgBody = Encoding.UTF8.GetString(message.Body); Console.WriteLine(string.Format("***接收时间:{0},消息内容:{1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), msgBody)); } channel.BasicAck(message.DeliveryTag, false); #手动释放 System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1)); }
我们再发一条消息,然后开始消费,加个断点调试一下
查看一下Queue中消息状态
然后直接取消调试,不让程序走到释放的那一步,再查看一下消息状态
这么说来只要不走到 channel.BasicAck(message.DeliveryTag, false);这一行,消息就不会被释放掉,我们让程序直接走到这一行代码,查看一下消息的状态
如图已经被释放了
7.让失败的消息回到队列中
while (true) { var message = channel.BasicGet(queue, false); if(message!=null) { var msgBody = Encoding.UTF8.GetString(message.Body); Console.WriteLine(string.Format("***接收时间:{0},消息内容:{1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), msgBody)); Console.WriteLine(message.DeliveryTag); #当前消息被处理的次序数 if (1==1) channel.BasicReject(message.DeliveryTag, true); } System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1)); }
重新发送4条消息
开始消费
我们可以看到消息一直没有没消费,因为消息被处理之后又放到了队尾
8.监听消息
using (IConnection conn = rabbitMqFactory.CreateConnection()) using (IModel channel = conn.CreateModel()) { channel.ExchangeDeclare(exchange, "direct", durable: true, autoDelete: false); channel.QueueDeclare(queue, durable: true, exclusive: false, autoDelete: false); channel.QueueBind(queue, exchange, route); channel.BasicQos(prefetchSize: 0, prefetchCount: 10, global: false); #一次接受10条消息,否则rabbit会把所有的消息一次性推到client,会增大client的负荷 EventingBasicConsumer consumer = new EventingBasicConsumer(channel); consumer.Received += (model, ea) => { Byte[] body = ea.Body; String message = Encoding.UTF8.GetString(body); Console.WriteLine( message+Thread.CurrentThread.ManagedThreadId); channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false); }; channel.BasicConsume(queue: queue, autoAck: false, consumer: consumer); Console.ReadLine(); }
ASP.NET Core 2.1 : 十五.图解路由(2.1 or earler)
本文通过一张图来看一下路由的配置以及请求处理的机制。(ASP.NET Core 系列目录)
一、概述
路由主要有两个主要功能:
- 将请求的URL与已定义的路由进行匹配,找到该URL对应的处理程序并传入该请求进行处理。
- 根据已定义的路由生成URL
这两个功能看起来这两个是相反的。
A.路由的配置
路由的两个功能都离不开一个基本的操作:路由的基本配置。在Startup中默认通过 routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}")定义,
当然我们还可以继续 routes.MapRoute(。。。); 这样就定义了一系列的路由匹配方式组成一个路由表,例如这样:
app.UseMvc(routes => { routes.MapRoute(name: "test", template: "Hello"); routes.MapRoute("flylolo/{code}/{name}", MyRouteHandler.Handler); routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });
每一个MapRoute会生成一个Route,第二个MapRoute看起来有些特殊,我们可以传入一个自定义的RequestDelegate(本例为MyRouteHandler.Handler)来处理“flylolo/{code}/{name}”这样的请求,
public static class MyRouteHandler { public static async Task Handler(HttpContext context) { await context.Response.WriteAsync("MyRouteHandler"); } }
它会被封装成一个RouteHandler(new RouteHandler(MyRouteHandler.Handler))赋值给Route的target属性,而对于另外两种没有指定的,Route的target属性默认会被指定为MvcRouteHandler ,如下图:
B.Handler的选择
当请求进入之后,根据此路由表对该URL进行逐一匹配,并将请求交给匹配到的路由的target(即MvcRouteHandler或RouteHandler),调用 _target.RouteAsync(context); ,在这个方法中,若是MvcRouteHandler会对请求的Controller和Action验证,若验证成功,则对context(是一个RouteContext)的Handler属性赋值一个匿名方法;若是RouteHandler则会直接将其封装的RequestDelegate(本例为MyRouteHandler.Handler)赋值给RouteContext.Handler.
C.请求处理
经过Handler的选择后,若RouteContext.Handler不为空,则调用RouteContext.Handler(HttpContext)对请求进行处理。
D.其他
回想一下中间件,这个是不是和app.Map("/test", XXHandle)这样配置中间件的方式有点像,当请求路径是/test的时候,请求交由XXHandle处理,同样是Map,对比着更容易理解。
下面通过一张图看一下路由配置和请求处理的流程。
二、流程及解析
为了方便查看,对几个“重点对象”做了颜色标识(点击图片可以看大图):
- 路由的初始化配置 一切从Startup开始,之前在中间件的文章中介绍过,一般是通过多个UseXXX的方式将多个中间件组成“请求处理管道”,而在这里通过UseMvc方法进行配置,传入routes.MapRoute(...)这样的一个或多个配置。
- 接下来会New一个
RouteBuilder
- ,顾名思义就是一个Route的创建者,通过调用传进来的一个或多个routes.MapRoute()方法生成多个Route,并配置默认的Handler。
var routes = new RouteBuilder(app) { DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(), }; configureRoutes(routes);//调用Startup中的routes.MapRoute(...)方法
①调用RouteBuilder的Build方法,生成一个RouteCollection。
public IRouter Build() { var routeCollection = new RouteCollection(); foreach (var route in Routes) { routeCollection.Add(route); } return routeCollection; }
②RouteCollection实现IRouteCollection和IRouter接口,他是在Startup中的配置组成的集合。
③RouterMiddleWare就是专门用于进行路由处理的中间件,在此将RouteCollection作为中间件RouterMiddleWare的参数,并将这个中间件插入管道中。
public class RouterMiddleware
{
private readonly IRouter _router; //就是RouteCollection
public async Task Invoke(HttpContext httpContext);
}
2. 请求处理流程
④请求的处理流程在RouterMiddleWare的invoke()方法中。
⑤请求首先会被封装成一个RouteContext,本质就是将httpContext、_router(也就是RouteCollection)包装到一个对象里。
var context = new RouteContext(httpContext); context.RouteData.Routers.Add(_router);
public class RouteContext { private RouteData _routeData; public RequestDelegate Handler ; public HttpContext HttpContext;//简单的赋值 public RouteData RouteData; }
⑥调用_router(也就是RouteCollection)的RouteAsync(context)方法,在其中遍历每一个路由
⑦若与请求URL匹配,则将对应的Handler赋值给context.Handler。
public async virtual Task RouteAsync(RouteContext context) { // 快照备份 var snapshot = context.RouteData.PushState(null, values: null, dataTokens: null); //遍历 for (var i = 0; i < Count; i++) { var route = this[i]; context.RouteData.Routers.Add(route); try { await route.RouteAsync(context);//若匹配,则给context.Handler赋值 if (context.Handler != null) { break; } } finally { if (context.Handler == null) { snapshot.Restore();//通过快照还原 } } } }
⑧在RouterMiddleWare的invoke()方法中,调用新赋值的context.Handler处理HttpContext;
httpContext.Features[typeof(IRoutingFeature)] = new RoutingFeature() { RouteData = context.RouteData, }; await context.Handler(context.HttpContext);
三、其他
由于文章写的比较早各种原因一直没有写完,现在发现2.2版本之后,启用了新的路由方案,还是把这章完成了发出来,有愿意看的可以参考一下,下一篇文章介绍一下2.2版的新的路由方案,至于通过路由生成URL部分,就暂时不写了。
.NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了
最近有个需求就是一个抽象仓储层接口方法需要SqlServer以及Oracle两种实现方式,为了灵活我在依赖注入的时候把这两种实现都给注入进了依赖注入容器中,但是在服务调用的时候总是获取到最后注入的那个方法的实现,这时候就在想能不能实现动态的选择使用哪种实现呢?如果可以的话那么我只需要在配置文件中进行相应的配置即可获取到正确的实现方法的调用,这样的话岂不快哉!今天我们就来一起探讨下实现这种需求的几种实现方式吧。
代码演示
在开始实现的方式之前,我们先模拟下代码。由于真实系统的结构比较复杂,所以这里我就单独建一个类似的项目结构代码。项目如下图所示:
接下来我来详细说下上面的结果作用及代码。
-
MultiImpDemo.I 这个项目是接口项目,里面有一个简单的接口定义
ISayHello
,代码如下:public interface ISayHello { string Talk(); }
很简单,就一个模拟讲话的方法。
-
MultiImpDemo.A 这个类库项目是接口的一种实现方式,里面有一个
SayHello
类用来实现ISayHello
接口,代码如下:/** *┌──────────────────────────────────────────────────────────────┐ *│ 描 述: *│ 作 者:yilezhu *│ 版 本:1.0 *│ 创建时间:2019/1/7 17:41:33 *└──────────────────────────────────────────────────────────────┘ *┌──────────────────────────────────────────────────────────────┐ *│ 命名空间: MultiImpDemo.A *│ 类 名: SayHello *└──────────────────────────────────────────────────────────────┘ */ using MultiImpDemo.I; using System; using System.Collections.Generic; using System.Text; namespace MultiImpDemo.A { public class SayHello : ISayHello { public string Talk() { return "Talk from A.SayHello"; } } }
-
MultiImpDemo.B 这个类库项目是接口的另一种实现方式,里面也有一个
SayHello
类用来实现ISayHello
接口,代码如下:/** *┌──────────────────────────────────────────────────────────────┐ *│ 描 述: *│ 作 者:yilezhu *│ 版 本:1.0 *│ 创建时间:2019/1/7 17:41:45 *└──────────────────────────────────────────────────────────────┘ *┌──────────────────────────────────────────────────────────────┐ *│ 命名空间: MultiImpDemo.B *│ 类 名: SayHello *└──────────────────────────────────────────────────────────────┘ */ using MultiImpDemo.I; using System; using System.Collections.Generic; using System.Text; namespace MultiImpDemo.B { public class SayHello:ISayHello { public string Talk() { return "Talk from B.SayHello"; } } }
-
MultiImpDemo.Show 这个就是用来显示我们模拟效果的API项目,首选我们在
ConfigureServices
中加入如下的代码来进行上述两种实现方式的注入:services.AddTransient<ISayHello, MultiImpDemo.A.SayHello>(); services.AddTransient<ISayHello, MultiImpDemo.B.SayHello>();
-
在api实现里面获取服务并进行模拟调用:
private readonly ISayHello sayHello; public ValuesController(ISayHello sayHello) { this.sayHello = sayHello; } // GET api/values [HttpGet] public ActionResult<IEnumerable<string>> Get() { return new string[] { sayHello.Talk() }; }
代码很简单对不对?你应该看的懂吧,这时候我们运行起来项目,然后访问API'api/values'这个接口,结果总是显示如下的结果:
两种需求对应两种实现
这里有两种业务需求!第一种业务中只需要对其中一种实现方式进行调用,如:业务需要SqlServer数据库的实现就行了。第二种是业务中对这两种实现方式都有用到,如:业务急需要用到Oracle的数据库实现同时也有用到SqlServer的数据库实现,需要同时往这两个数据库中插入相同的数据。下面分别对这两种需求进行解决。
业务中对这两种实现方式都有用到
针对这种情况有如下两种实现方式:
-
第二种实现方式
其实,在ASP.NET Core中,当你对一个接口注册了多个实现的时候,构造函数是可以注入一个该接口集合的,这个集合里是所有注册过的实现。
下面我们先改造下
ConfigureServices
,分别注入下这两种实现services.AddTransient<ISayHello, A.SayHello>(); services.AddTransient<ISayHello,B.SayHello>();
接着继续改造下注入的方式,这里我们直接注入
IEnumerable<ISayHello>
如下代码所示:private readonly ISayHello sayHelloA; private readonly ISayHello sayHelloB; public ValuesController(IEnumerable<ISayHello> sayHellos) { sayHelloA = sayHellos.FirstOrDefault(h => h.GetType().Namespace == "MultiImpDemo.A"); sayHelloB = sayHellos.FirstOrDefault(h => h.GetType().Namespace == "MultiImpDemo.B"); } // GET api/values [HttpGet] public ActionResult<IEnumerable<string>> Get() { return new string[] { sayHelloA.Talk() , sayHelloB.Talk()}; }
然后运行起来看下效果吧
-
利用
AddTransient
的扩展方法public static IServiceCollection AddTransient<TService>(this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory) where TService : class;
然后根据我们的配置的实现来进行服务实现的获取。下面就让我们利用代码来实现一番吧:services.AddTransient<A.SayHello>(); services.AddTransient<B.SayHello>(); services.AddTransient(implementationFactory => { Func<string, ISayHello> accesor = key => { if (key.Equals("MultiImpDemo.A")) { return implementationFactory.GetService<A.SayHello>(); } else if (key.Equals("MultiImpDemo.B")) { return implementationFactory.GetService<B.SayHello>(); } else { throw new ArgumentException($"Not Support key : {key}"); } }; return accesor; });
当然了,既然用到了我们配置文件中的代码,因此我们需要设置下这个配置:
然后我们具体调用的依赖注入的方式需要变化一下:
private readonly ISayHello sayHelloA; private readonly ISayHello sayHelloB; private readonly Func<string, ISayHello> _serviceAccessor; public ValuesController(Func<string, ISayHello> serviceAccessor) { this._serviceAccessor = serviceAccessor; sayHelloA = _serviceAccessor("MultiImpDemoA"); sayHelloB = _serviceAccessor("MultiImpDemoB"); } // GET api/values [HttpGet] public ActionResult<IEnumerable<string>> Get() { return new string[] { sayHelloA.Talk() , sayHelloB.Talk()}; }
然后运行看下效果吧:
可以看到A跟B的实现都获取到了!效果实现!
业务只需要对其中一种实现方式的调用
这时候我们可以根据我们预设的配置来动态获取我们所需要的实现。这段话说的我自己都感觉拗口。话不多少,开鲁吧!这里我将介绍三种实现方式。
-
根据我们的配置文件中设置的key来进行动态的注入。
这种方式实现之前首先得进行相应的配置,如下所示:
"CommonSettings": { "ImplementAssembly": "MultiImpDemo.A" }
然后在注入的时候根据配置进行动态的进行注入:
services.AddTransient<ISayHello, A.SayHello>(); services.AddTransient<ISayHello, B.SayHello>();
然后在服务调用的时候稍作修改:
private readonly ISayHello sayHello; public ValuesController(IEnumerable<ISayHello> sayHellos,IConfiguration configuration) { sayHello = sayHellos.FirstOrDefault(h => h.GetType().Namespace == configuration.GetSection("CommonSettings:ImplementAssembly").Value); } // GET api/values [HttpGet] public ActionResult<IEnumerable<string>> Get() { return new string[] { sayHello.Talk() }; }
OK,到这里运行一下看下效果吧!然后改下配置文件再看下效果!
-
第二种实现方式,即接口参数的方式这样可以避免上个方法中反射所带来的性能损耗。
这里我们改造下接口,接口中加入一个程序集的属性,如下所示:
public interface ISayHello { string ImplementAssemblyName { get; } string Talk(); }
对应的A跟B中的实现代码也要少做调整:
A:
public string ImplementAssemblyName => "MultiImpDemo.A"; public string Talk() { return "Talk from A.SayHello"; }
B:
public string ImplementAssemblyName => "MultiImpDemo.B"; public string Talk() { return "Talk from B.SayHello"; }
然后,在实现方法调用的时候稍微修改下:
private readonly ISayHello sayHello; public ValuesController(IEnumerable<ISayHello> sayHellos,IConfiguration configuration) { sayHello = sayHellos.FirstOrDefault(h => h.ImplementAssemblyName == configuration.GetSection("CommonSettings:ImplementAssembly").Value); } // GET api/values [HttpGet] public ActionResult<IEnumerable<string>> Get() { return new string[] { sayHello.Talk() }; }
效果自己运行下看下吧!
-
第三种实现是根据配置进行动态的注册
首先修改下
ConfigureServices
方法:var implementAssembly = Configuration.GetSection("CommonSettings:ImplementAssembly").Value; if (string.IsNullOrWhiteSpace(implementAssembly)) throw new ArgumentNullException("CommonSettings:ImplementAssembly未配置"); if (implementAssembly.Equals("MultiImpDemo.A")) { services.AddTransient<ISayHello, A.SayHello>(); } else { services.AddTransient<ISayHello, B.SayHello>(); }
这样的话就会根据我们的配置文件来进行动态的注册,然后我们像往常一样进行服务的调取即可:
private readonly ISayHello _sayHello; public ValuesController(ISayHello sayHello) { _sayHello = sayHello; } // GET api/values [HttpGet] public ActionResult<IEnumerable<string>> Get() { return new string[] { _sayHello.Talk() }; }
运行即可得到我们想要的效果!
总结
本文从具体的业务需求入手,根据需求来或动态的进行对应服务的获取,或同时使用两个不同的实现!希望对您有所帮助!如果您有更多的实现方法可以在下方留言,或者加入.NET Core实战千人群跟637326624大伙进行交流,最后感谢您的阅读!