4. ASP.NET Core默认服务

之前讲了中间件,实际上一个中间件要正常进行工作,通常需要许多的服务配合进行,而中间件中的服务自然也是通过 Ioc 容器进行注册和注入的。前面也讲到,按照约定中间件的封装一般会提供一个 User{Middleware} 的扩展方法给用户使用,而服务注册中也有一个类似的约定,一般会有一个 Add{Services} 的扩展方法。

例如一个WebApi项目中,对于控制器路由终结点中间件的配置使用:

builder.Services.AddControllers();

var app = builder.Build();
app.MapControllers();

这也是我们在日常开发中可以学习的方式,随着业务增长,需要依赖注入的服务也越来越多,我们可以根据业务模块,通过扩展方法讲相应模块的服务注入注册进行封装,命名为 Add{Services},更加清晰明了地对我们的业务进行封装。

.NET Core 框架下默认提供250个以上的的服务,包括 ASP.NET Core MVC、EF Core 等等,当然这些服务很多不会默认就注入到容器中,我们在新建一个项目的时候,不同项目框架的模板会帮我们默认配置好一些最基本的必须的服务,其他的服务我们可以根据自己的需要进行使用。

5. 依赖注入配置变形

随着业务的增长,我们项目工作中的类型、服务越来越多,而每一个服务的依赖注入关系都需要在入口文件通过Service.Add{}方法去进行注册,这将是非常麻烦的,入口文件需要频繁改动,而且代码组织管理也会变得麻烦,非常不优雅。

在许多框架中会对这种通过 Service.Add{xxx} 的方式在代码中显式注册依赖注入关系的方式进行变形,有的可以通过配置文件进行注册,例如 Java Spring 框架就有这样大量的配置文件,有的可以通过接口进行默认注册,有的通过特性进行默认注册。

这里稍微简单介绍一下依赖注入默认注册的原理,其实也就是通过放射的一些手段,再加上一些约定好的规则而已。

首先需要三个生命周期接口,如下,这三个接口没有内容,仅仅只是作为标记而已。

public interface ISingleton
{
}
public interface IScoped
{
}
public interface ITransient
{
}

之后需要一个扩展方法,如下:

namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionDependencyExtensions
{
public static IServiceCollection AddAutoInject<T>(this IServiceCollection services)
{
var register = new ServiceRegister();
register.AddAssembly(services, typeof(T).Assembly);
return services;
}
}
}

这个扩展方法中调用了注册器,往容器中注入服务,实现如下:

public class ServiceRegister
{
public void AddAssembly(IServiceCollection services, Assembly assembly)
{
// 查找程序中的类型
var types = assembly.GetTypes().Where(t => t != null && t.IsClass && !t.IsAbstract && !t.IsGenericType);
// 遍历每一个类检查释放满足约定的规则
foreach (var type in types)
{
AddType(services, type);
}
} /// <summary>
/// 添加当前类型的依赖注入关系
/// </summary>
/// <param name="services"></param>
/// <param name="type"></param>
public void AddType(IServiceCollection services, Type type)
{
var lifetime = GetLifetimeOrNull(type);
if (lifetime == null)
{
return; var exposeServices = ExposeService(type);
foreach (var serviceType in exposeServices)
{
var serviceDescriptor = new ServiceDescriptor(serviceType, type, lifetime.Value);
services.Add(serviceDescriptor);
}
} /// <summary>
/// 根据标记接口确定生命周期,如果没有添加标记接口的,则不会被自动注册到容器
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public ServiceLifetime? GetLifetimeOrNull(Type type)
{
if (typeof(ISingleton).IsAssignableFrom(type))
{
return ServiceLifetime.Singleton;
}
if(typeof(IScoped).IsAssignableFrom(type))
{
return ServiceLifetime.Scoped;
}
if(typeof(ITransient).IsAssignableFrom(type))
{
return ServiceLifetime.Transient;
}
return null;
}
/// <summary>
/// 根据约定的规则查找当前类对于的服务类型
/// 通过接口实现的方式,查找当前类实现的接口,如果一个接口名称去除了 "I" 之后与当前类的后半段一样,
/// 则当前类应该被注册为这个接口的服务。
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public IList<Type> ExposeService(Type type)
{
var serviceTypes = new List<Type>();
var interfaces = type.GetInterfaces();
foreach (var interfacesType in interfaces)
{
var interfaceName = interfacesType.Name;
if (interfaceName.StartsWith("I"))
{
interfaceName = interfaceName.Substring(1);
}
if (type.Name.EndsWith(interfaceName))
{
serviceTypes.Add(interfacesType);
}
}
return serviceTypes;
}
}

整体的逻辑就是查找遍历程序集中的所有类型,并通过判别类型是否实现之前定好的三个生命周期接口,从而确定类型是否需要自动注册到容器中,如果需要再根据约定好的规则获取需要注册的服务类型,并且构建服务描述器,再将其添加到容器中。

之后在入口文件中这样使用:

builder.Services.AddAutoInject<Program>();

而需要自动注入的服务只要多实现一个标记接口即可:

public class Rabbit : IRabbit, ITransient
{
}

以上主要介绍一下依赖注入自动化注册的思路和基本实现,代码只是一个基本的演示,比较简单,很多细节也没有在这里体现,但是核心的思路是和ABP框架中的自动注入的方式一样的,有兴趣详细了解一下ABP中的依赖注入的机制的童鞋,可以看一下我其他的文章: ABP 依赖注入(1)

参考文章:

ASP.NET Core 依赖注入 | Microsoft Learn

理解ASP.NET Core - 依赖注入(Dependency Injection)

ASP.NET Core 系列:

目录:ASP.NET Core 系列总结

上一篇:ASP.NET Core - 依赖注入(三)

ASP.NET Core - 依赖注入(四)的更多相关文章

  1. # ASP.NET Core依赖注入解读&使用Autofac替代实现

    标签: 依赖注入 Autofac ASPNETCore ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Aut ...

  2. 实现BUG自动检测 - ASP.NET Core依赖注入

    我个人比较懒,能自动做的事绝不手动做,最近在用ASP.NET Core写一个项目,过程中会积累一些方便的工具类或框架,分享出来欢迎大家点评. 如果以后有时间的话,我打算写一个系列的[实现BUG自动检测 ...

  3. [译]ASP.NET Core依赖注入深入讨论

    原文链接:ASP.NET Core Dependency Injection Deep Dive - Joonas W's blog 这篇文章我们来深入探讨ASP.NET Core.MVC Core中 ...

  4. asp.net core 依赖注入几种常见情况

    先读一篇注入入门 全面理解 ASP.NET Core 依赖注入, 学习一下基本使用 然后学习一招, 不使用接口规范, 直接写功能类, 一般情况下可以用来做单例. 参考https://www.cnblo ...

  5. ASP.NET Core依赖注入——依赖注入最佳实践

    在这篇文章中,我们将深入研究.NET Core和ASP.NET Core MVC中的依赖注入,将介绍几乎所有可能的选项,依赖注入是ASP.Net Core的核心,我将分享在ASP.Net Core应用 ...

  6. 自动化CodeReview - ASP.NET Core依赖注入

    自动化CodeReview系列目录 自动化CodeReview - ASP.NET Core依赖注入 自动化CodeReview - ASP.NET Core请求参数验证 我个人比较懒,能自动做的事绝 ...

  7. ASP.NET Core 依赖注入最佳实践——提示与技巧

    在这篇文章,我将分享一些在ASP.NET Core程序中使用依赖注入的个人经验和建议.这些原则背后的动机如下: 高效地设计服务和它们的依赖. 预防多线程问题. 预防内存泄漏. 预防潜在的BUG. 这篇 ...

  8. ASP.NET Core依赖注入最佳实践,提示&技巧

    分享翻译一篇Abp框架作者(Halil İbrahim Kalkan)关于ASP.NET Core依赖注入的博文. 在本文中,我将分享我在ASP.NET Core应用程序中使用依赖注入的经验和建议. ...

  9. ASP.NET Core依赖注入解读&使用Autofac替代实现【转载】

    ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Autofac实现和自定义实现扩展方法 3.1 安装Autof ...

  10. ASP.NET Core 依赖注入基本用法

    ASP.NET Core 依赖注入 ASP.NET Core从框架层对依赖注入提供支持.也就是说,如果你不了解依赖注入,将很难适应 ASP.NET Core的开发模式.本文将介绍依赖注入的基本概念,并 ...

随机推荐

  1. Redis如何模糊匹配Key值

    Redis模糊匹配Key值 使用Redis的scan代替Keys指令: public Set<String> scan(String matchKey) { Set<String&g ...

  2. Day38:Lambda表达式

    Lambda表达式 1.1 概述 Lambda是JDK8开始后的一种新语法形式. 作用:简化函数式匿名内部类的代码写法. 简化格式: /*部类被重写方法的参数)->{ 被重写方法的方法体代码 } ...

  3. markdown语法使用

    markdown语法使用 标题系列 ​ 1.警号 ​ 2.快捷键 ​ ctrl + 数字(1~6) 小标题系列 * 文本 无序标题 + 文本 无序标题 数字 文本 有序标题 语言环境 表格制作 表情制 ...

  4. for循环结构、range方法

    目录 今日内容总结 whlie补充说明 for循环结构 range方法 练习 今日内容总结 whlie补充说明 1.死循环 真正死循环是一旦执行 CPU功耗会急速上升 直到系统采取紧急措施 尽量不要让 ...

  5. json提取器和beanshell处理器组合,将提取的所有id以数组返回

    1.添加json提取器 2.添加beanshell处理器,并编写脚本 String str1 = vars.get("buildid_ALL"); log.info(str1); ...

  6. python进阶之路2——解释器软件安装

    内容概要 计算机五大组成部分 计算机三大核心硬件 操作系统 编程与编程语言 编程语言发展史 编程语言的分类 python解释器下载与安装 python解释器多版本共存 pycharm安装 计算机五大组 ...

  7. Coolify系列-手把手教学解决局域网局域网中的其他主机访问虚拟机以及docker服务

    背景 我在windows电脑安装了一个VM,使用VM开启了Linux服务器,运行docker,下载服务镜像,然后运行服务,然后遇到了主机无法访问服务的问题. 问题排查 STEP1:首先要开启防火墙端口 ...

  8. 关于 Dev-C++ 中缺少 iconv.h 的问题

    前言 在 C++ 中有个扩展库 ext,里面有一些黑科技(hash, splay, binomial_heap 等等), 在 Windows 环境中,我们运行 Dev-C++ 并在头文件写 #incl ...

  9. Java语言发展史-计算机进制转换

    Java语言发展史 java的诞生 在1991年时候,James Gosling在Sun公司的工程师小组想要设计这样一种主要用于像电视盒这样的消费类电子产品的小型计算机语言. 这些电子产品有一个共同的 ...

  10. 静态static关键字概述-静态static关键字修饰成员变量

    静态static关键字概述 概述 关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属 于某个对象的.也就是说,既然属于类,就可以不靠创建对象来 ...