实现BUG自动检测

发表于:2018-9-14 11:56

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:ysum6846    来源:CSDN

  我个人比较懒,能自动做的事绝不手动做,最近在用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}");
  }
  }
  }
  }
  }
  }
  }
  }


   上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号