用Scrutor来简化ASP.NET Core的DI注册
目录
- 背景
- Scrutor简介
- Scrutor的简单使用
- 注册接口的实现类
- 注册类自身
- 重复注册处理策略
- 总结
- 相关文章
背景
在我们编写ASP.NET Core代码的时候,总是离不开依赖注入这东西。而且对于这一块,我们有非常多的选择,比如:M$ 的DI,Autofac,Ninject,Windsor 等。
由于M$自带了一个DI框架,所以一般情况下都会优先使用。虽说功能不是特别全,但也基本满足使用了。
正常情况下(包括好多示例代码),在要注册的服务数量比较少时,我们会选择一个一个的去注册。
好比下面的示例:
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<IUserService, UserService>();
在数量小于5个的时候,这样的做法还可以接受,但是,数量一多,还这样子秀操作,可就有点接受不了了。
可能会经常出现这样的问题,新加了一个东西,忘记在Startup
上面注册,下一秒得到的就是类似下面的错误:
System.InvalidOperationException: Unable to resolve service for type 'ScrutorTest.IProductRepository' while attempting to activate 'ScrutorTest.Controllers.ValuesController'.at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)at lambda_method(Closure , IServiceProvider , Object[] )at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.<CreateActivator>b__0(ControllerContext controllerContext)at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
这样一来一回,其实也是挺浪费时间的。
为了避免这种情况,我们往往会根据规律在注册的时候,用反射进行批量注册,后面按照对应的规律去写业务代码,就可以避免上面这种问题了。
对于这个问题,本文将介绍一个扩展库,来帮我们简化这些操作。
Scrutor简介
Scrutor是 Kristian Hellang 大神写的一个基于Microsoft.Extensions.DependencyInjection的一个扩展库,主要是为了简化我们对DI的操作。
Scrutor主要提供了两个扩展方法给我们使用,一个是Scan
,一个是Decorate
。
本文主要讲的是Scan
这个方法。
Scrutor的简单使用
注册接口的实现类
这种情形应该是我们用的最多的一种,所以优先来说这种情况。
假设我们有下面几个接口和实现类,
public interface IUserService { }
public class UserService : IUserService { }public interface IUserRepository { }
public class UserRepository : IUserRepository { }public interface IProductRepository { }
public class ProductRepository : IProductRepository { }
现在我们只需要注册UserRepository
和ProductRepository
,
services.Scan(scan => scan.FromAssemblyOf<Startup>().AddClasses(classes => classes.Where(t=>t.Name.EndsWith("repository",StringComparison.OrdinalIgnoreCase))).AsImplementedInterfaces().WithTransientLifetime());
简单解释一下,上面的代码做了什么事:
FromAssemblyOf<Startup>
表示加载Startup
这个类所在的程序集AddClasses
表示要注册那些类,上面的代码还做了过滤,只留下了以 repository 结尾的类AsImplementedInterfaces
表示将类型注册为提供其所有公共接口作为服务WithTransientLifetime
表示注册的生命周期为 Transient
如果了解过Autofac的朋友,看到这样的写法应该很熟悉。
对于上面的例子,它等价于下面的代码
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<IProductRepository, ProductRepository>();
如果我们在注册完成后,想看一下我们自己注册的信息,可以加上下面的代码:
var list = services.Where(x => x.ServiceType.Namespace.Equals("ScrutorTest", StringComparison.OrdinalIgnoreCase)).ToList();foreach (var item in list)
{Console.WriteLine($"{item.Lifetime},{item.ImplementationType},{item.ServiceType}");
}
运行dotnet run
之后,可以看到下面的输出
Singleton,ScrutorTest.UserRepository,ScrutorTest.IUserRepository
Singleton,ScrutorTest.ProductRepository,ScrutorTest.IProductRepository
这个时候,如果我们加了一个 IOrderRepository
和 OrderRepostity
, 就不需要在Startup上面多写一行注册代码了,Scrutor已经帮我们自动处理了。
接下来,我们需要把UserService
也注册进去,我们完全可以照葫芦画瓢了。
services.Scan(scan => scan.FromAssemblyOf<Startup>().AddClasses(classes => classes.Where(t=>t.Name.EndsWith("repository",StringComparison.OrdinalIgnoreCase))).AsImplementedInterfaces().WithTransientLifetime());services.Scan(scan => scan.FromAssemblyOf<Startup>().AddClasses(classes => classes.Where(t => t.Name.EndsWith("service", StringComparison.OrdinalIgnoreCase))).AsImplementedInterfaces().WithTransientLifetime());
也可以略微简单一点点,一个scan里面搞定所有
services.Scan(scan => scan.FromAssemblyOf<Startup>().AddClasses(classes => classes.Where(t=>t.Name.EndsWith("repository",StringComparison.OrdinalIgnoreCase))).AsImplementedInterfaces().WithTransientLifetime().AddClasses(classes => classes.Where(t => t.Name.EndsWith("service", StringComparison.OrdinalIgnoreCase))).AsImplementedInterfaces().WithScopedLifetime()//换一下生命周期);
这个时候结果如下:
Transient,ScrutorTest.UserRepository,ScrutorTest.IUserRepository
Transient,ScrutorTest.ProductRepository,ScrutorTest.IProductRepository
Scoped,ScrutorTest.UserService,ScrutorTest.IUserService
虽然效果一样,但是总想着有没有一些更简单的方法。
很多时候,我们写一些接口和实现类的时候,都会根据这样的习惯来命名,定义一个接口IClass
,它的实现类就是Class
。
针对这种情形,Scrutor提供了一个简便的方法来帮助我们处理。
使用 AsMatchingInterface
方法就可以很轻松的帮我们处理注册好对应的信息。
services.Scan(scan => scan.FromAssemblyOf<Startup>().AddClasses().AsMatchingInterface().WithTransientLifetime());
这个时候会输出下面的结果:
Transient,ScrutorTest.UserService,ScrutorTest.IUserService
Transient,ScrutorTest.UserRepository,ScrutorTest.IUserRepository
Transient,ScrutorTest.ProductRepository,ScrutorTest.IProductRepository
当然这种方法也有对应的缺点,那就是对生命周期的控制。举个例子,有两大类,一大类要Transient,一大类要Scoped,这个时候,我们也只能过滤掉部分内容才能注册 。
需要根据自身的情况来选择是否要使用这个方法,或者什么时候使用这个方法。
注册类自身
有时候,我们建的一些类是没有实现接口的,就纯粹是在“裸奔”的那种,然后直接用单例的方式来调用。
Scrutor也提供了方法AsSelf
来处理这种情形。
来看下面这段代码。
services.Scan(scan => scan.AddTypes(typeof(MyClass)).AsSelf().WithSingletonLifetime());
这里和前面的注册代码有一点点差异。
AddTypes
是直接加载具体的某个类或一批类,这个的作用可以认为和FromXxx
是一样的。
它等价于下面的代码
services.AddSingleton<MyClass>();
相对来说批量操作的时候还是有点繁锁,因为需要把每个类型都扔进去,我们不可能事先知道所有的类。
下面的方法可以把MyClass
所在的程序集的类都注册了。
services.Scan(scan => scan.FromAssemblyOf<MyClass>().AddClasses().AsSelf().WithSingletonLifetime());
这样的做法也有一个缺点,会造成部分我们不想让他注册的,也注册进去了。
过滤一下或者规范一下自己的结构,就可以处理这个问题了。
重复注册处理策略
还有一个比较常见的情形是,重复注册,即同一个接口,有多个不同的实现。
Scrutor提供了三大策略,Append、Skip和Replace。 Append是默认行为,就是叠加。
下面来看这个例子
public interface IDuplicate { }
public class FirstDuplicate : IDuplicate { }
public class SecondDuplicate : IDuplicate { }
services.Scan(scan => scan.FromAssemblyOf<Startup>() .AddClasses(classes=>classes.AssignableTo<IDuplicate>()).AsImplementedInterfaces().WithTransientLifetime()
);
这个时候的输出如下
Transient,ScrutorTest.FirstDuplicate,ScrutorTest.IDuplicate
Transient,ScrutorTest.SecondDuplicate,ScrutorTest.IDuplicate
下面我们用Skip策略来替换默认的策略
services.Scan(scan => scan.FromAssemblyOf<Startup>() .AddClasses(classes=>classes.AssignableTo<IDuplicate>())//手动高亮.UsingRegistrationStrategy(RegistrationStrategy.Skip).AsImplementedInterfaces().WithTransientLifetime()
);
这个时候的输出如下
Transient,ScrutorTest.FirstDuplicate,ScrutorTest.IDuplicate
可见得到的结果确实没有了第二个注册。
总结
Scrutor的Scan方法确实很方便,可以让我们很容易的扩展M$ 的DI。
当然Scrutor还有其他的用法,详细的可以参考它的Github页面。
相关文章
Introducing Scrutor - Convention based registration for Microsoft.Extensions.DependencyInjection
Using Scrutor to automatically register your services with the ASP.NET Core DI container
转载于:https://www.cnblogs.com/catcher1994/p/10316928.html
用Scrutor来简化ASP.NET Core的DI注册相关推荐
- asp.net core利用DI实现自定义用户系统,脱离ControllerBase.User
前言 很多时候其实我们并不需要asp.net core自带的那么复杂的用户系统,基于角色,各种概念,还得用EF Core,而且在web应用中都是把信息存储到cookie中进行通讯(我不喜欢放cooki ...
- 使用Azure AD B2C为ASP.NET Core 设置登录/注册
一,引言 上次关于Azure AD B2C 讲到一些概念,有介绍到,Azure AD B2C 也是一种身份验证的解决方案,但是它运行客户使用其首选的社交,企业或者本地账户标识对应用程序和API进行单一 ...
- ASP.NET CORE的服务注册方法
首先是编辑接口 完了之后就是实现接口的方法 在之后就是在 Startup类的ConfigureServices方法中注册,我目前的理解就是让某个接口绑定给某个控制器,然后该控制器就能使用这个接口里的方 ...
- ASP.NET Core学习资源汇总
ASP.NET Core入门学习资源汇总篇幅比較長,分为七个部分. (一)认识.NET Core (二)Vistual Studio安装.调试 (三)Asp.Net Core入门指南与学习路线 (四) ...
- 如何利用Serilog的RequestLogging来精简ASP.NET Core的日志输出
这是该系列的第一篇文章:在ASP.NET Core 3.0中使用Serilog.AspNetCore. 第1部分-使用Serilog RequestLogging来简化ASP.NET Core的日志输 ...
- 在Asp.Net Core中使用ModelConvention实现全局过滤器隔离
从何说起 这来自于我把项目迁移到Asp.Net Core的过程中碰到一个问题.在一个web程序中同时包含了MVC和WebAPI,现在需要给WebAPI部分单独添加一个接口验证过滤器IActionFil ...
- 【笔记】ASP.NET Core技术内幕与项目实现:基于DDD与前后端分离
最近在写论文,想使用ASP.NET Core Web API技术,但对它还不是很熟,鉴权组件也没用过,于是在网上查找资料,发现了杨中科老师写的这本书(微信读书上可以免费看),说起来我最初自学C#时看过 ...
- ASP.NET Core 2 学习笔记(四)依赖注入
原文:ASP.NET Core 2 学习笔记(四)依赖注入 ASP.NET Core使用了大量的依赖注入(Dependency Injection, DI),把控制反转(Inversion Of Co ...
- 什么是Asp.net Core?和 .net core有什么区别?
为什么要写这篇文章 写这篇文章有两个原因,第一个是因为新站点创建出来后一直空置着,所以写一篇文章放在这里.第二就是因为近来在做一些基于Asp.net core平台的项目开发,也遇到了一些问题,正好趁此 ...
最新文章
- Linux内核中网络数据包的接收-第一部分 概念和框架
- tx2 undefined reference to PyExc_ImportError'
- MySQL通信类型:同步或者异步
- ASPxGridView EditFormLayout修改 TextBox文本长度
- markdown的11个语法
- 学习jvm,关于MAT an internal error occurred during:Parsing heap dump from问题
- 二项式反演(非详细)
- 远控免杀专题(15)-DKMC免杀
- (2)python_enumerate
- WEB API异常处理
- 【数字信号去噪】基于matlab小波软阈值+硬阈值+改进阈值轴承故障仿真信号去噪【含Matlab源码 1024期】
- 转:飞思卡尔单片机RAM与flash相关问题
- 动画设计的12条基本原理
- Android面试Hash原理详解二
- TI OMAP4XXX 协议处理器(4460应用于三星、华为等手机)
- 很累的时候要坚强,但别逞强
- java咖啡标志_50个包含咖啡的创意LOGO设计
- VS Code 插件 在线/离线 下载安装VUE
- java如何将两首歌按时间合成,用goldwave怎样把两首歌合并在一起
- 资源 | 适合AI新手的9款人工智能开源软件
热门文章
- mysql blob 内容查看_这些被你忽视的MySQL细节,可能会让你丢饭碗!
- python读取文件夹下所有图片_python 读取单文件夹中的图片文件信息保存到csv文件中...
- Linux 基础命令上
- 接口幂等性问题解决方案
- 0xc000007b:vs2012+Opencv2.4.4出现0xc000007b问题
- iOS11 UITableViewCell滑动事件改动
- shell 与 空格
- 如何理解VMware内存资源管理
- 视频上传、转码、切面、存储的思路
- ubuntu下面markdown转化为word文档