自动化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. (转)获取 request 中用POST方式"Content-type"是"application/x-www-form-urlencoded;charset=utf-8"发送的 json 数据

    request中发送json数据用post方式发送Content-type用application/json;charset=utf-8方式发送的话,直接用springMVC的@RequestBody ...

  2. python tkinter-窗体

    1.导入自带的包名 import tkinter 2.创建一个窗体对象 form=Tkinter.Tk() 3.显示窗体(这句应该是所有的设置部署完最后执行的一句代码) form.mainloop() ...

  3. IPV4闪退

    如果出现这种状况,在安全模式下重注册dll 运行->输入cmd->输入 for %1 in (%windir%\system32\*.dll) do regsvr32.exe /s %1 ...

  4. Web轻量级扫描工具Skipfish

    Web轻量级扫描工具Skipfish 1. Skipfish 简介 2. Skipfish 基本操作 3.身份认证 一. Skipfish 简介 Skipfish是一款主动的Web应用程序安全侦察工具 ...

  5. ThreadPoolExecutor 源码阅读

    目录 ThreadPoolExecutor 源码阅读 Executor 框架 Executor ExecutorService AbstractExecutorService 构造器 状态 Worke ...

  6. ruby那些事儿

    为了学习sass,客串一下ruby,这是一个破案的过程,探索的过程. Ruby是日本人发明的一种类似于Python和Perl的服务器端脚本语言,它完全面向对象,适合于快速开发,一般开发效率是java的 ...

  7. C# 运行中 Lua 语言脚本

    这里就不介绍Lua语言了,使用挺广的一种脚本语言.自行百度. 第一步 使用 Nuget 安装引用 VikingErik.LuaInterface. 第二步 添加 Using using LuaInte ...

  8. Mysql 创建用户并授权

    设置MySQL用户账号,指定哪个用户可以连接服务器,从哪里连接,连接后能做什么. MySQL管理员应该知道如何设置MySQL用户账号,指出哪个用户可以连接服务器,从哪里连接,连接后能做什么? GRAN ...

  9. Codeforces Round #406 (Div. 1) B. Legacy 线段树建图跑最短路

    B. Legacy 题目连接: http://codeforces.com/contest/786/problem/B Description Rick and his co-workers have ...

  10. 各种Oracle索引类型介绍

    逻辑上:Single column 单行索引Concatenated 多行索引Unique 唯一索引NonUnique 非唯一索引Function-based函数索引Domain 域索引 物理上:Pa ...