自动化CodeReview系列目录

  1. 自动化CodeReview - ASP.NET Core依赖注入
  2. 自动化CodeReview - ASP.NET Core请求参数验证

我个人比较懒,能自动做的事绝不手动做,最近在用ASP.NET Core写一个项目,过程中会积累一些方便的工具类或框架,分享出来欢迎大家点评。

如果以后有时间的话,我打算写一个系列的【实现BUG自动检测】,本文将是第一篇。

如果你使用过ASP.NET Core那么对依赖注入一定不陌生。
使用流程为:
1. 先注册Service,有3个方法AddTransient、AddScoped、AddSingleton
2. 再使用Service,通常在构造方法里声明

先来说说产生BUG的场景
BUG场景一:
有的时候可能因为疏忽忘记注册Service直接就使用了,使用那个Service时会报异常。这种情况项目都是可以编译通过的,是一个不太容易发现的BUG,如果那个Service在测试时没有覆盖到这个BUG就会被带到生产环境

BUG场景二:
通常有一些Service我们只希望它在请求作用域内被使用,例如:在服务端持有数据库连接的Service通常都是请求作用域级别的,即:在请求内第一次使用数据库时创建数据库连接,请求内会复用连接,请求结束回收连接。
对应ASP.NET Core里的注册方式如下:
services.AddScoped<IDbContext, DbContext>();

在ASP.NET Core中AddScoped注册的Service在请求结束时会销毁。
如果你在控制器中直接引用IDbContext一切正常,现在业务需要我们要封装一个用户管理类UserManager,它是单例的,注册代码:
services.AddScoped<IUserManager, UserManager>();

在写UserManager类的时候要访问数据库,顺手就引用了IDbContext(正常是不应该这么引用的但是忘记了),因为UserManager是单例会造成IDbContext永远不会释放,进而长期占用一个数据库连接。并且在编译时,运行时都不会报错,很隐蔽的一个BUG

好了,场景说完了,本文的主角该登场了,解决方式如下:
在Startup类的ConfigureServices方法最后加入如下代码:

public void ConfigureServices(IServiceCollection services){
//此处省略若干代码... //确保服务依赖的正确性,放到所有注册服务代码后调用
if (_env.IsDevelopment())
services.AssertDependencyValid();
} 

对于“场景一”此方法会抛出异常:
throw new InvalidProgramException($"服务 {svceType.FullName} 的构造方法引用了未注册的服务 {paramType.FullName}");

对于“场景二”此方法会抛出异常:
throw new InvalidProgramException($"Singleton的服务 {svceType.FullName} 的构造方法引用了Scoped的服务 {paramType.FullName}");

您可以根据异常的提示找到具体有问题的类并修改之

完整代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; namespace Microsoft.Extensions.DependencyInjection
{
public static class MondolServiceCollectionExtensions
{
/// <summary>
/// DUMP服务列表
/// </summary>
public static string Dump(this IServiceCollection services)
{
var sevList = new List<Tuple<string, string>>();
foreach (var sev in services)
{
sevList.Add(new Tuple<string, string>(sev.Lifetime.ToString(), sev.ServiceType.FullName));
}
sevList.Sort((x, y) =>
{
var cRs = string.CompareOrdinal(x.Item1, y.Item1);
return cRs != 0 ? cRs : string.CompareOrdinal(x.Item2, y.Item2);
}); return string.Join("\r\n", sevList.Select(p => $"{p.Item2} - {p.Item1}"));
} /// <summary>
/// 确保当前注册服务的依赖关系是正确的
/// </summary>
public static void AssertDependencyValid(this IServiceCollection services)
{
var ignoreTypes = new[]
{
"Microsoft.AspNetCore.Mvc.Internal.MvcRouteHandler",
"Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperDescriptorResolver"
}; foreach (var svce in services)
{
if (svce.Lifetime == ServiceLifetime.Singleton)
{
//确保Singleton的服务不能依赖Scoped的服务
if (svce.ImplementationType != null)
{
var svceType = svce.ImplementationType;
if (ignoreTypes.Contains(svceType.FullName))
continue; var ctors = svceType.GetConstructors();
foreach (var ctor in ctors)
{
var paramLst = ctor.GetParameters();
foreach (var param in paramLst)
{
var paramType = param.ParameterType;
var paramTypeInfo = paramType.GetTypeInfo();
if (paramTypeInfo.IsGenericType)
{
if (paramType.ToString().StartsWith("System.Collections.Generic.IEnumerable`1"))
{
paramType = paramTypeInfo.GetGenericArguments().First();
paramTypeInfo = paramType.GetTypeInfo();
}
}
if (paramType == typeof(IServiceProvider))
continue; ServiceDescriptor pSvce;
if (paramTypeInfo.IsGenericType)
{
//泛型采用模糊识别,可能有遗漏
var prefix = Regex.Match(paramType.ToString(), @"^[^`]+`\d+\[").Value;
pSvce = services.FirstOrDefault(p => p.ServiceType.ToString().StartsWith(prefix));
}
else
{
pSvce = services.FirstOrDefault(p => p.ServiceType == paramType);
}
if (pSvce == null)
throw new InvalidProgramException($"服务 {svceType.FullName} 的构造方法引用了未注册的服务 {paramType.FullName}");
if (pSvce.Lifetime == ServiceLifetime.Scoped)
throw new InvalidProgramException($"Singleton的服务 {svceType.FullName} 的构造方法引用了Scoped的服务 {paramType.FullName}");
}
}
}
}
}
}
}
}

  

自动化CodeReview - ASP.NET Core依赖注入的更多相关文章

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

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

  2. 自动化CodeReview - ASP.NET Core请求参数验证

    自动化CodeReview系列目录 自动化CodeReview - ASP.NET Core依赖注入 自动化CodeReview - ASP.NET Core请求参数验证 参数验证实现 在做服务端开发 ...

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

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

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

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

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

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

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

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

随机推荐

  1. HDU3342Legal or Not 拓扑排序

    有向图判断是否成环  如果是环输出NO 只要入度为0的点的个数 等于 总的点的个数则无环 #include<bits/stdc++.h> using namespace std; //in ...

  2. Immediate Decodability HDU1305

    类似phonelist  一次ac 判断失败主要有两个要点 1. 是否包含了某段的结尾结点   说明某段被此段包含 2.此段的结尾结点是否为某段的痕迹   说明此段被包含 #include<bi ...

  3. 移动端滑屏全应用【一】cssHandler操作基础动画函数封装

    前言: 大家都知道,在移动端进行操作结点移动时,我们都会使用操作transform来代替top等用以提高性能,必要的时候还可开启3d加速.我们都会使用getComputedStyle来获取结点的最终样 ...

  4. HQL count(*)

    public int getTarPage() {        String hql = "'";        Query query = getSession().creat ...

  5. 算法初级面试题05——哈希函数/表、生成多个哈希函数、哈希扩容、利用哈希分流找出大文件的重复内容、设计RandomPool结构、布隆过滤器、一致性哈希、并查集、岛问题

    今天主要讨论:哈希函数.哈希表.布隆过滤器.一致性哈希.并查集的介绍和应用. 题目一 认识哈希函数和哈希表 1.输入无限大 2.输出有限的S集合 3.输入什么就输出什么 4.会发生哈希碰撞 5.会均匀 ...

  6. 获取AFP服务信息

    获取AFP服务信息   如果苹果系统开放TCP 548端口,说明其开启了AFP服务.这个时候,可以使用Nmap的afp-serverinfo脚本获取对应的服务信息.获取的信息包括服务名.机器类型.AF ...

  7. BZOJ.2177.曼哈顿最小生成树(Kruskal)

    \(Solution\) 参考 对于每个点,向唯一有可能与它形成MST的8个点连边,由于是双向单边,所以每个点最多连出4条边(证明见blog) 怎么找到一个区域内最近的点? 只考虑y轴右侧45°的区域 ...

  8. Python图形编程探索系列-06-按钮批量生产函数

    设计任务 初步设计一个批量生产按钮的函数,根据需要的按钮数量,自动生成多少按钮. 函数设计 import tkinter as tk # 导入tkinter库 root = tk.Tk() # 建立程 ...

  9. Python 爬取bangumi网页信息

    1.数据库连接池 #######db.py########## import time import pymysql import threading from DBUtils.PooledDB im ...

  10. linux VIM 下的语法高亮及自动缩进

    显示行号 set number 自动缩进有两个选项 set autoindent set cindent autoindent 就是自动缩进的意思,当你在输入状态用回车键插入一个新行,或者在 norm ...