轻量级.Net Core服务注册工具CodeDi发布啦
为什么做这么一个工具
因为我们的系统往往时面向接口编程的,所以在开发Asp .net core项目的时候,一定会有大量大接口及其对应的实现要在ConfigureService注册到ServiceCollection中,传统的做法是加了一个服务,我们就要注册一次(service.AddService()),又比如,当一个接口有多个实现,在构造函数中获取服务也不是很友好,而据我所知, .Net Core目前是没有什么自带的库或者方法解决这些问题,当然,如果引入第三方容器如AutoFac这些问题时能迎刃而解的,但是如何在不引入第三方容器来解决这个问题呢?
所以我就设计了这样的一个轻量级工具.
首先,放上该项目的Github地址(记得Star哦!!)
https://github.com/liuzhenyulive/CodeDi
CodeDi是一个基于 .Net Standard的工具库,它能帮助我们自动地在Asp .net core或者 .net core项目中完成服务的注册.
Overview
CodeDi 是 Code Dependency Injection的意思,在上次我在看了由依乐祝写的<.NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了>后,回想起我之前遇到的那些问题,感觉拨云见日,所以,我就开始着手写这个工具了.
如何使用CodeDi
安装Nuget包
CodeDi的Nuget包已经发布到了 nuget.org,您可以通过以下指令在您的项目中安装CodeDi
PM> Install-Package CodeDi
ConfigureServices中的配置
方法 1
您可以在Startup的ConfigureService方法中添加AddCodeDi完成对CodeDi的调用.服务的注册CodeDi会自动为您完成.
public void ConfigureServices(IServiceCollection services)
{
services.AddCoreDi();
services.AddMvc();
}
方法 2
您也可以在AddCodeDi方法中传入一个Action<CodeDiOptions>参数,在这个action中,您可以对CodeDiOptions的属性进行配置.
public void ConfigureServices(IServiceCollection services)
{
services.AddCoreDi(options =>
{
options.DefaultServiceLifetime = ServiceLifetime.Scoped;
});
services.AddMvc();
}
方法 3
当然您也可以直接给AddCodeDi()方法直接传入一个CodeDiOptions实例.
public void ConfigureServices(IServiceCollection services)
{
services.AddCoreDi(new CodeDiOptions()
{
DefaultServiceLifetime = ServiceLifetime.Scoped
});
services.AddMvc();
}
你也可以在appsetting.json文件中配置CodeDiOptions的信息,并通过Configuration.Bind("CodeDiOptions", options)把配置信息绑定到一个CodeDiOptions实例.
appsetting.json file
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"CodeDiOptions": {
"DefaultServiceLifetime": 1,
"AssemblyNames": [
"*CodeDi"
],
"AssemblyPaths": [
"C:\\MyBox\\Github\\CodeDI\\CodeDI\\bin\\Debug\\netstandard2.0"
],
"IgnoreAssemblies": [
"*Test"
],
"IncludeSystemAssemblies": false,
"IgnoreInterface": [
"*Say"
],
"InterfaceMappings": {
"*Say": "*English"
},
"ServiceLifeTimeMappings": {
"*Say": 0
}
}
}
ConfigureService方法
public void ConfigureServices(IServiceCollection services)
{
var options=new CodeDiOptions();
Configuration.Bind("CodeDiOptions", options);
services.AddCoreDi(options);
services.AddMvc();
}
CodeDiOptions详解
| 属性名称 | 属性描述 | 数据类型 | 默认值 |
|---|---|---|---|
| AssemblyPaths | 在指定目录下加载Dll程序集 | string[] | Bin目录 |
| AssemblyNames | 选择要加载的程序集名称 (支持通配符) | string[] | * |
| IgnoreAssemblies | 忽略的程序集名称 (支持通配符) | string[] | null |
| IncludeSystemAssemblies | 是否包含系统程序集(当为false时,会忽略含有System,Microsoft,CppCodeProvider,WebMatrix,SMDiagnostics,Newtonsoft关键词和在App_Web,App_global目录下的程序集) | bool | false |
| IgnoreInterface | 忽略的接口 (支持通配符) | string[] | null |
| InterfaceMappings | 接口对应的服务 (支持通配符) ,当一个接口有多个实现时,如果不进行配置,则多个实现都会注册到SerciceCollection中 | Dictionary<string, string> | null |
| DefaultServiceLifetime | 默认的服务生命周期 | ServuceLifetime( Singleton,Scoped,Transient) | ServiceLifetime.Scope |
| ServiceLifeTimeMappings | 指定某个接口的服务生命周期,不指定为默认的生命周期 | Dictionary<string, ServiceLifetime> | null |
InterfaceMappings
如果 ISay 接口有SayInChinese 和SayInEnglish 两个实现,我们只想把SayInEnglish注册到ServiceCollection中
public interface ISay
{
string Hello();
}
public class SayInChinese:ISay
{
public string Hello()
{
return "您好";
}
}
public class SayInEnglish:ISay
{
public string Hello()
{
return "Hello";
}
}
那么我们可以这样配置InterfaceMappings.
options.InterfaceMappings=new Dictionary<string, string>(){{ "ISay", "SayInChinese" } }
也就是{接口名称(支持通配符),实现名称(支持通配符)}
ServiceLifeTimeMappings
如果我们希望ISay接口的服务的生命周期为Singleton,我们可以这样配置ServiceLifeTimeMappings.
options.ServiceLifeTimeMappings = new Dictionary<string, ServiceLifetime>(){{"*Say",ServiceLifetime.Singleton}};
也就是也就是{接口名称(支持通配符),Servicelifetime}
关于ServiceLifetime: https://github.com/aspnet/DependencyInjection/blob/master/src/DI.Abstractions/ServiceLifetime.cs
获取服务实例
当然, 您可以和之前一样,直接在构造函数中进行依赖的注入,但是当某个接口有多个实现而且都注册到了ServiceCollection中,获取就没有那么方便了,您可以用ICodeDiServiceProvider 来帮助您获取服务实例.
例如,当 ISay 接口有 SayInChinese 和 SayInEnglish两个实现, 我们我们如何获取我们想要的服务实例呢?
public interface ISay
{
string Hello();
}
public class SayInChinese:ISay
{
public string Hello()
{
return "您好";
}
}
public class SayInEnglish:ISay
{
public string Hello()
{
return "Hello";
}
}
public class HomeController : Controller
{
private readonly ISay _say;
public HomeController(ICodeDiServiceProvider serviceProvider)
{
_say = serviceProvider.GetService<ISay>("*Chinese");
}
public string Index()
{
return _say.Hello();
}
}
ICodeDiServiceProvider.GetService<T>(string name=null)
参数中的Name支持通配符.
CodeDi如何实现的?
既然是一个轻量级工具,那么实现起来自然不会太复杂,我来说说比较核心的代码.
private Dictionary<Type, List<Type>> GetInterfaceMapping(IList<Assembly> assemblies)
{
var mappings = new Dictionary<Type, List<Type>>();
var allInterfaces = assemblies.SelectMany(u => u.GetTypes()).Where(u => u.IsInterface);
foreach (var @interface in allInterfaces)
{
mappings.Add(@interface, assemblies.SelectMany(a =>
a.GetTypes().
Where(t =>
t.GetInterfaces().Contains(@interface)
)
)
.ToList());
}
return mappings;
}
GetInterfaceMapping通过反射机制,首先获取程序集中的所有接口allInterfaces,然后遍历allInterfaces找到该接口对应的实现,最终,该方法返回接口和实现的匹配关系,为Dictionary<Type, List>类型的数据.
private void AddToService(Dictionary<Type, List<Type>> interfaceMappings)
{
foreach (var mapping in interfaceMappings)
{
if (mapping.Key.FullName == null || (_options.IgnoreInterface != null &&
_options.IgnoreInterface.Any(i => mapping.Key.FullName.Matches(i))))
continue;
if (mapping.Key.FullName != null && _options.InterfaceMappings != null &&
_options.InterfaceMappings.Any(u => mapping.Key.FullName.Matches(u.Key)))
{
foreach (var item in mapping.Value.Where(value => value.FullName != null).
Where(value => value.FullName.Matches(_options.InterfaceMappings.FirstOrDefault(u => mapping.Key.FullName.Matches(u.Key)).Value)))
{
AddToService(mapping.Key, item);
}
continue;
}
foreach (var item in mapping.Value)
{
AddToService(mapping.Key, item);
}
}
}
该方法要判断CodeDiOptions中是否忽略了该接口,同时,是否指定实现映射关系.
什么叫实现映射关系呢?参见InterfaceMappings
如果指定了,那么就按指定的来实现,如果没指定,就会把每个实现都注册到ServiceCollection中.
private readonly IServiceCollection _service;
private readonly CodeDiOptions _options;
private readonly ServiceDescriptor[] _addedService;
public CodeDiService(IServiceCollection service, CodeDiOptions options)
{
_service = service ?? throw new ArgumentNullException(nameof(service));
_options = options ?? new CodeDiOptions();
_addedService = new ServiceDescriptor[service.Count];
service.CopyTo(_addedService, 0);
//在构造函数中,我们通过这种方式把Service中已经添加的服务读取出来
//后面进行服务注册时,会进行判断,避免重复添加
}
private void AddToService(Type serviceType, Type implementationType)
{
ServiceLifetime serviceLifetime;
try
{
serviceLifetime = _options.DefaultServiceLifetime;
if (_options.ServiceLifeTimeMappings != null && serviceType.FullName != null)
{
var lifeTimeMapping =
_options.ServiceLifeTimeMappings.FirstOrDefault(u => serviceType.FullName.Matches(u.Key));
serviceLifetime = lifeTimeMapping.Key != null ? lifeTimeMapping.Value : _options.DefaultServiceLifetime;
}
}
catch
{
throw new Exception("Service Life Time Only Can be set in range of 0-2");
}
if (_addedService.Where(u => u.ServiceType == serviceType).Any(u => u.ImplementationType == implementationType))
return;
_service.Add(new ServiceDescriptor(serviceType, implementationType, serviceLifetime));
}
AddToService中,要判断有没有对接口的生命周期进行配置,参见ServiceLifeTimeMappings,如果没有配置,就按DefaultServiceLifetime进行配置,DefaultServiceLifetime如果没有修改的情况下时ServiceLifetime.Scoped,即每个Request创建一个实例.
private readonly IServiceProvider _serviceProvider;
public CodeDiServiceProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public T GetService<T>(string name) where T : class
{
return _serviceProvider.GetService<IEnumerable<T>>().FirstOrDefault(u => u.GetType().Name.Matches( name));
}
这CodeDiServiceProvider的实现代码,这里参考了依乐祝写的<.NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了>给出的一种解决方案,即当某个接口注册了多个实现,其实可以通过IEnumerable获取所有的实现,CodeDiServiceProvider对其进行了封装.
Enjoy it
只要进行一次简单的CodeDi配置,以后系统中添加了新的接口以及对应的服务实现后,就不用再去一个个地Add到IServiceCollection中了.
如果有问题,欢迎Issue,欢迎PR.
最后,赏个Star呗! 前往Star
轻量级.Net Core服务注册工具CodeDi发布啦的更多相关文章
- .NET Core 服务诊断工具
前言: 前一篇文中介绍了.NET Core-全局性能诊断工具 的使用方法,那么接下来自己实现一个简单.NET Core的诊断工具. 该工具主要包括:.NET Core 程序进程信息查看.性能计数器结果 ...
- .net core 服务注册生命周期
在Asp.Net core中的IServiceCollection容器中注册服务的生命周期分以下3种: 1.Transient 通过AddTransient注册,会在IServiceCollectio ...
- spring-cloud-eureka服务注册与发现
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的.SpringCloud将它集成在其子项 ...
- .net core grpc consul 实现服务注册 服务发现 负载均衡(二)
在上一篇 .net core grpc 实现通信(一) 中,我们实现的grpc通信在.net core中的可行性,但要在微服务中真正使用,还缺少 服务注册,服务发现及负载均衡等,本篇我们将在 .net ...
- .Net Core Grpc Consul 实现服务注册 服务发现 负载均衡
本文是基于..net core grpc consul 实现服务注册 服务发现 负载均衡(二)的,很多内容是直接复制过来的,..net core grpc consul 实现服务注册 服务发现 负载均 ...
- spring cloud+.net core搭建微服务架构:服务注册(一)
背景 公司去年开始使用dotnet core开发项目.公司的总体架构采用的是微服务,那时候由于对微服务的理解并不是太深,加上各种组件的不成熟,只是把项目的各个功能通过业务层面拆分,然后通过nginx代 ...
- ASP.NET Core MVC 2.x 全面教程_ASP.NET Core MVC 03. 服务注册和管道
ASP.NET Core MVC 2.x 全面教程_ASP.NET Core MVC 03. 服务注册和管道 语雀: https://www.yuque.com/yuejiangliu/dotnet/ ...
- 温故知新,.Net Core遇见Consul(HashiCorp),实践分布式服务注册与发现
什么是Consul 参考 https://www.consul.io https://www.hashicorp.com 使用Consul做服务发现的若干姿势 ASP.NET Core 发布之后通过命 ...
- 我是服务的执政官-服务发现和注册工具consul简介
服务发现和注册 我们有了两个服务.服务A的IP地址是192.168.0.1,端口9001,服务B的IP地址192.168.0.2,端口9002.我们的客户端需要调用服务A和服务B,我们只需要在配置文件 ...
随机推荐
- python笔记:#013#高级变量类型
高级变量类型 目标 列表 元组 字典 字符串 公共方法 变量高级 知识点回顾 Python 中数据类型可以分为 数字型 和 非数字型 数字型 整型 (int) 浮点型(float) 布尔型(bool) ...
- SpringBoot 创建可执行Jar
创建可执行JAR 我们也可以通过插件创建一个在生产环境中运行的可执行jar文件来完成我们的示例. 首先引入依赖: <build> <plugins> <plugin> ...
- 小白突破百度翻译反爬机制,33行Python代码实现汉译英小工具!
表弟17岁就没读书了,在我家呆了差不多一年吧. 呆的前几个月,每天上网打游戏,我又不好怎么在言语上管教他,就琢磨着看他要不要跟我学习Python编程.他开始问我Python编程什么?我打开了我给学生上 ...
- 填坑!!!virtualenv 中 nginx + uwsgi 部署 django
一.为什么会有这篇文章 第一次接触 uwsgi 和 nginx ,这个环境搭建,踩了太多坑,现在记录下来,让后来者少走弯路. 本来在 Ubuntu14.04 上 搭建好了环境,然后到 centos7. ...
- (Lesson2)根据类名称和属性获得元素-JavaScript面向对象
描述:在编写选择器的时候遇到的一根问题,我需要实现Jquery的选择器功能,第一个根据ID获取Element非常简单,第二个根据类(class)去获取Element集合,这个相对复杂,而根据name和 ...
- Java NIO Buffer缓冲区
原文链接:http://tutorials.jenkov.com/java-nio/buffers.html Java NIO Buffers用于和NIO Channel交互.正如你已经知道的,我们从 ...
- Oracle-07:别名,去重,子查询
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 依旧提供数据库脚本供操作 create table DEPT ( deptno ) not null, dna ...
- JavaWeb(一)JavaWeb应用的概念
JavaWeb应用的概念 在Sun的Java Servlet规范中,对Java Web应用作了这样定义:"Java Web应用由一组Servlet.HTML页.类.以及其它可以被绑定的资源构 ...
- c# xml操作(二)
c# xml操作(二) 此博文包含图片 (-- ::)转载▼ 标签: 杂谈 分类: c# 上次,我们介绍了增加和删除xml的一些操作,这次我们将介绍如何更改和读取xml特定节点.我们依然以上次的xml ...
- 浅谈MySQL存储引擎-InnoDB&MyISAM
存储引擎在MySQL的逻辑架构中位于第三层,负责MySQL中的数据的存储和提取.MySQL存储引擎有很多,不同的存储引擎保存数据和索引的方式是不同的.每一种存储引擎都有它的优势和劣势,本文只讨论最常见 ...