今天教大家如何在asp .net core 和 .net 控制台程序中 批量注入服务和 BackgroundService 后台服务
在默认的 .net 项目中如果我们注入一个服务或者后台服务,常规的做法如下
注册后台服务
builder.Services.AddHostedService<ClearLogTask>();
针对继承自接口的服务进行注入:
builder.Services.AddTransient<IOperationTransient, Operation>();
builder.Services.AddScoped<IOperationScoped, Operation>();
builder.Services.AddSingleton<IOperationSingleton, Operation>();
builder.Services.AddSingleton(new Operation());
builder.Services.AddScoped(typeof(Operation));
builder.Services.AddTransient(typeof(Operation));
针对非继承自接口的无构造函数类进行注入
builder.Services.AddSingleton(new Operation());
builder.Services.AddSingleton(typeof(Operation));
builder.Services.AddScoped(typeof(Operation));
builder.Services.AddTransient(typeof(Operation));
针对非继承自接口的有构造函数的类进行注入(此类型只支持进行单例注入)
builder.Services.AddSingleton(new Operation("参数1","参数2"));

 

上面是常见的几种在项目启动时注入服务的写法,当项目存在很多服务的时候,我们需要一条条的注入显然太过繁琐,所以今天来讲一种批量注入的方法,本文使用的是微软默认的DI 没有去使用 AutoFac ,个人喜欢大道至简,能用官方实现的,就尽量的少去依赖第三方的组件,下面直接展示成果代码。
 
    public static class IServiceCollectionExtension
{ public static void BatchRegisterServices(this IServiceCollection services)
{
var allAssembly = GetAllAssembly(); services.RegisterServiceByAttribute(ServiceLifetime.Singleton, allAssembly);
services.RegisterServiceByAttribute(ServiceLifetime.Scoped, allAssembly);
services.RegisterServiceByAttribute(ServiceLifetime.Transient, allAssembly); services.RegisterBackgroundService(allAssembly);
} /// <summary>
/// 通过 ServiceAttribute 批量注册服务
/// </summary>
/// <param name="services"></param>
/// <param name="serviceLifetime"></param>
private static void RegisterServiceByAttribute(this IServiceCollection services, ServiceLifetime serviceLifetime, List<Assembly> allAssembly)
{ List<Type> types = allAssembly.SelectMany(t => t.GetTypes()).Where(t => t.GetCustomAttributes(typeof(ServiceAttribute), false).Length > 0 && t.GetCustomAttribute<ServiceAttribute>()?.Lifetime == serviceLifetime && t.IsClass && !t.IsAbstract).ToList(); foreach (var type in types)
{ Type? typeInterface = type.GetInterfaces().FirstOrDefault(); if (typeInterface == null)
{
//服务非继承自接口的直接注入
switch (serviceLifetime)
{
case ServiceLifetime.Singleton: services.AddSingleton(type); break;
case ServiceLifetime.Scoped: services.AddScoped(type); break;
case ServiceLifetime.Transient: services.AddTransient(type); break;
}
}
else
{
//服务继承自接口的和接口一起注入
switch (serviceLifetime)
{
case ServiceLifetime.Singleton: services.AddSingleton(typeInterface, type); break;
case ServiceLifetime.Scoped: services.AddScoped(typeInterface, type); break;
case ServiceLifetime.Transient: services.AddTransient(typeInterface, type); break;
}
} } } /// <summary>
/// 注册后台服务
/// </summary>
/// <param name="services"></param>
/// <param name="serviceLifetime"></param>
private static void RegisterBackgroundService(this IServiceCollection services, List<Assembly> allAssembly)
{ List<Type> types = allAssembly.SelectMany(t => t.GetTypes()).Where(t => typeof(BackgroundService).IsAssignableFrom(t) && t.IsClass && !t.IsAbstract).ToList(); foreach (var type in types)
{
services.AddSingleton(typeof(IHostedService), type);
}
} /// <summary>
/// 获取全部 Assembly
/// </summary>
/// <returns></returns>
private static List<Assembly> GetAllAssembly()
{ var allAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList(); HashSet<string> loadedAssemblies = new(); foreach (var item in allAssemblies)
{
loadedAssemblies.Add(item.FullName!);
} Queue<Assembly> assembliesToCheck = new();
assembliesToCheck.Enqueue(Assembly.GetEntryAssembly()!); while (assembliesToCheck.Any())
{
var assemblyToCheck = assembliesToCheck.Dequeue();
foreach (var reference in assemblyToCheck!.GetReferencedAssemblies())
{
if (!loadedAssemblies.Contains(reference.FullName))
{
var assembly = Assembly.Load(reference); assembliesToCheck.Enqueue(assembly); loadedAssemblies.Add(reference.FullName); allAssemblies.Add(assembly);
}
}
} return allAssemblies;
} } [AttributeUsage(AttributeTargets.Class)]
public class ServiceAttribute : Attribute
{
public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Transient; }
实现的逻辑其实并不复杂,首先利用循环检索找出项目中所有的 Assembly
获取项目所有 Assembly 这个方法,需要格外注意,因为 .NET 项目在启动的时候并不会直接把所有 dll 都进行加载,甚至有时候项目经过分层之后服务可能分散于多个类库中,所以我们这里需要循环的将项目所有的 Assembly 信息全部查询出来,确保万无一失。
当找到全部的 Assembly 之后只要查询中 包含我们指定的 ServiceAttribute 装饰属性的类和 继承自 BackgroundService 类型的所有类型,然后进行依次注入即可。
主要在原先的服务类头部加上
[Service(Lifetime = ServiceLifetime.Scoped)]
[Service(Lifetime = ServiceLifetime.Singleton)]
[Service(Lifetime = ServiceLifetime.Transient)]

像下面的 AuthorizeService 只要只要在头部加上 [Service(Lifetime = ServiceLifetime.Scoped)]

 [Service(Lifetime = ServiceLifetime.Scoped)]
public class AuthorizeService
{ private readonly DatabaseContext db;
private readonly SnowflakeHelper snowflakeHelper;
private readonly IConfiguration configuration; public AuthorizeService(DatabaseContext db, SnowflakeHelper snowflakeHelper, IConfiguration configuration)
{
this.db = db;
this.snowflakeHelper = snowflakeHelper;
this.configuration = configuration;
} /// <summary>
/// 通过用户id获取 token
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public string GetTokenByUserId(long userId)
{
//此处省略业务逻辑
} }

至于注册后台服务,则连装饰属性都不需要加,如下面的的一个后台服务示例代码

public class ClearLogTask : BackgroundService
{ private readonly IServiceProvider serviceProvider; public DemoTask(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
} protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
return Task.Run(() =>
{
var timer = new Timer(1000 * 5);
timer.Elapsed += TimerElapsed;
timer.Start();
}, stoppingToken);
} private void TimerElapsed(object? sender, ElapsedEventArgs e)
{
//省略业务逻辑
}
}
像上面的这个清理日志服务,每5秒钟会执行一次,按照微软的语法所有的后台服务都是继承自 BackgroundService 类型的。
然后我们项目启动的时候只要调用一下我们写的批量注册服务扩展方法即可。这样就批量完成了对项目中所有的服务和后台服务的注入。
builder.Services.BatchRegisterServices();
至此 .NET 使用自带 DI 批量注入服务(Service) 和 后台服务(BackgroundService)就讲解完了,有任何不明白的,可以在文章下面评论或者私信我,欢迎大家积极的讨论交流,有兴趣的朋友可以关注我目前在维护的一个 .net 基础框架项目,项目地址如下

.NET 使用自带 DI 批量注入服务(Service)和 后台服务(BackgroundService)的更多相关文章

  1. .netcore之DI批量注入(支持泛型) - xms

    一旦系统内模块比较多,按DI标准方法去逐个硬敲AddScoped/AddSingleton/AddTransient缺乏灵活性且效率低下,所以批量注入提供了很大的便捷性,特别是对于泛型的服务类,下面介 ...

  2. 利用ASP.netCore自带DI(DependencyInjection)实现批量依赖注入

    ASP.net Core自带DI(依赖注入),用法如下: services.AddScoped(typeof(IProductService), typeof(ProductService)); 如果 ...

  3. Android系统编程入门系列之服务Service齐头并进多线程任务

    在上篇文章中初步了解了Android系统的四大组件之一的服务Service,在服务内可以执行无用户交互的耗时操作任务,但是包括之前关于界面系列文章在内,生命周期方法都是在主线程内被系统回调的.如果直接 ...

  4. ASP.NET Core Web 应用程序系列(三)- 在ASP.NET Core中使用Autofac替换自带DI进行构造函数和属性的批量依赖注入(MVC当中应用)

    在上一章中主要和大家分享了在ASP.NET Core中如何使用Autofac替换自带DI进行构造函数的批量依赖注入,本章将和大家继续分享如何使之能够同时支持属性的批量依赖注入. 约定: 1.仓储层接口 ...

  5. ASP.NET Core Web 应用程序系列(二)- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用)

    在上一章中主要和大家分享在MVC当中如何使用ASP.NET Core内置的DI进行批量依赖注入,本章将继续和大家分享在ASP.NET Core中如何使用Autofac替换自带DI进行批量依赖注入. P ...

  6. 从我做起[原生DI实现模块化和批量注入].Net Core 之一

    实现模块化注册 .Net Core实现模块化批量注入 我将新建一个项目从头开始项目名称Sukt.Core. 该项目分层如下: Sukt.Core.API 为前端提供APi接口(里面尽量不存在业务逻辑, ...

  7. 在.NET Core中批量注入Grpc服务

    GRPC 是谷歌发布的一个开源.高性能.通用RPC服务,尽管大部分 RPC 框架都使用 TCP 协议,但其实 UDP 也可以,而 gRPC 干脆就用了 HTTP2.还有就是它具有跨平台.跨语言 等特性 ...

  8. .netCore2.0 程序集DI依赖注入

    传统的依赖注入确实简单,但是随着项目的扩展随之而来的问题又来了,因为传统的注入是单个类和接口注入的,加入项目的接口和类增加到了上百个的话,就需要在Startup.cs中复制注入上百次,虽然能解决问题, ...

  9. IoC控制反转与DI依赖注入

    IoC控制反转与DI依赖注入 IoC: Inversion of Control IoC是一种模式.目的是达到程序的复用.下面的两篇论文是对IoC的权威解释: InversionOfControl h ...

随机推荐

  1. Go语言学习——map

    map 映射关系容器 内部使用散列表(hash)实现 map是引用类型 必须初始化才能使用 无序的基于key-value的数据结构 map定义 map的定义语法: map[KeyType]ValueT ...

  2. 团队Beta1

    队名:观光队 链接 组长博客 作业博客 组员实践情况 王耀鑫 **过去两天完成了哪些任务 ** 文字/口头描述 任务分配 展示GitHub当日代码/文档签入记录 接下来的计划 完成短租车,页面美化 * ...

  3. font-family样式对照表

    .a { font-family: "微软雅黑" } .b { font-family: "黑体" } .c { font-family: "楷体&q ...

  4. Vulnhub-earth

    1.信息收集 1.1找到目标主机ip vulnhub靶场通用的技巧 这里我们靶场是nat模式的 所以肯定就是在我们自己设置的一个网段范围内,我这里nat本机的地址是10.1.1.1 所处的网段就是10 ...

  5. mysql5.7介绍和安装

    环境准备: 1.关闭防火墙和selinux systemctl stop firewalldsystemctl stop SElinux 2. 如果安装过mariadb需要停止且卸载服务 system ...

  6. 《C Primer Plus》第六版笔记--4~6章

    目录 第四章 字符串和格式化输入/输出 第五章 运算符.表达式和语句 第六章 C控制语句:循环 虽然匆匆忙忙,但还是要做笔记,虽然大概都知道...... 挑一些容易忘记的地方 第四章 字符串和格式化输 ...

  7. JAVA - 缓冲和缓存

    JAVA - 缓冲和缓存 缓冲 Buffer 功能:协调上下层应用之间的性能差异.通过缓冲区的缓冲,当上层组件性能优于下层组件的时候,缓冲可以有效减少上层组件对下层组件的等待时间. 使用场景:IO流中 ...

  8. 【摸鱼神器】UI库秒变LowCode工具——列表篇(二)维护json的小工具

    上一篇介绍了一下如何实现一个可以依赖 json 渲染的列表控件,既然需要 json 文件,那么要如何维护这个 json 文件就成了重点,如果没有好的维护方案的话,那么还不如直接用UI库. 所以需要我们 ...

  9. Arraylist集合、对象数组

    Arraylist集合 ArrayList是List接口的一个实现类,它是程序中最常见的一种集合. 他的特点:在增加或删除指定位置的元素时,会创建新的数组,效率比较低,因此不适合做大量的增删操作,Ar ...

  10. TypeScript(3)基础类型

    基础类型 TypeScript 支持与 JavaScript 几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用. 布尔值 最基本的数据类型就是简单的true/false值,在JavaScri ...