写点什么

在 ASP.NET Core 引入 Scrutor 增强依赖注入

作者:雄鹿 @
  • 2024-03-26
    广东
  • 本文字数:3456 字

    阅读完需:约 11 分钟

在 ASP.NET Core 引入 Scrutor 增强依赖注入

本文章介绍在 ASP.NET Core 中使用 Scrutor 来增强内置依赖注入框架

先决条件

.NET 8 SDK

介绍

Scrutor 并不是一个依赖注入框架,而是用来增强 ASP.NET Core 的内置依赖注入框架

在使用 ASP.NET Core 内置依赖注入框架注册服务时,只能一个一个地进行注册

无法像 Autofac 一样拥有强大的注册方式,而 Scrutor 主要的功能就是为了解决这一痛点

Scrutor 的卖点是它扫描程序集的方法,并自动注册它找到的类型

安装 Scrutor

通过命令行安装 Scrutor

dotnet add package Scrutor
复制代码

准备被扫描的程序集

创建了一个类库名为 Serivce,编译以后的就会生成 Service.dll 的程序集文件

其中有四个文件

#region IScanAssemblyMarker.cs 文件的代码	// 此接口用来查找程序集    public interface IScanAssemblyMarker { }#endregion
#region OrderService.cs 文件的代码 public interface IOrderService { } public class OrderService : IOrderService { }#endregion
#region UserService.cs 文件的代码 public interface IUserService { Task<User> GetUserByIdAsync(int userId); } public async Task<User> GetUserByIdAsync(int userId) => await Task.FromResult(new User { Id = userId, Name = $"UserName_{userId}" });#endregion
#region InvoiceRepository.cs 文件的代码 public interface IInvoiceRepository { } public class InvoiceRepository : IInvoiceRepository { }#endregion
复制代码

使用 Scrutor 进行程序集扫描注册

直接上一个示例代码,该代码依赖于上面的 Serivce 类库

    static void Main(string[] args)    {        var collection = new ServiceCollection();        collection.Scan(action =>         	action.FromAssemblyOf<IScanAssemblyMarker>()        	            .AddClasses(@class => @class.Where(t => t.Name.EndsWith("Service")))            .UsingRegistrationStrategy(RegistrationStrategy.Throw)            .AsMatchingInterface()            .WithScopedLifetime()                        .AddClasses(@class => @class.Where(t => t.Name.EndsWith("Repository")))            .UsingRegistrationStrategy(RegistrationStrategy.Throw)            .AsMatchingInterface()            .WithSingletonLifetime());            foreach (var item in collection)        {            var log = $"Service: {item.ServiceType.FullName} Lifetime: {item.Lifetime}\nInstance: {item.ImplementationType?.FullName}\n";            Console.WriteLine(log);        }    }
复制代码


在进行程序集扫描注册时,有五个步骤

  1. 找到程序集

  2. 选择并筛选要注册的服务

  3. 设置重复注册服务的替换策略

  4. 将实现注册为服务并指定策略

  5. 指定已注册类的生存期

找到程序集

在示例代码中,action.FromAssemblyOf<IScanAssemblyMarker>() 为找到 IScanAssemblyMarker 这个接口所在的程序集

找到程序集还有几个另外的扩展方法,如 FromAssemblyDependencies(Assembly assembly) 可以直接传入程序集

选择并筛选要注册的服务

在示例代码中,AddClasses(@class => @class.Where(t => t.Name.EndsWith("Service"))) 为选择并筛选要注册的服务,筛选出为 Service 字符结尾的类名

设置重复注册服务的替换策略

然后是 UsingRegistrationStrategy(RegistrationStrategy.Throw) ,设置重复注册服务的替换策略

这里 RegistrationStrategy.Throw 为 尝试注册现有服务时引发异常。

当然你可以设置为已经注册过的就直接选择跳过(RegistrationStrategy.Skip)或者附加(RegistrationStrategy.Append

将实现注册为服务并指定策略

然后是示例代码中的 AsMatchingInterface() 将实现注册为服务并指定策略

这里我使用的 AsMatchingInterface() 相当于 services.AddSingleton<IClassName, ClassName>()

如果这里使用 AsSelf() 相当于 services.AddSingleton<ClassName>()

如果一个类实现了多个接口,可以使用 AsImplementedInterfaces()

这相当于:

services.AddSingleton<IClassName, ClassName>()

services.AddSingleton<IName, ClassName>()

指定已注册类的生存期

指定生命周期使用 WithSingletonLifetime() 为单例

指定生命周期使用 WithScopedLifetime() 为作用域

指定生命周期使用 WithTransientLifetime() 为瞬时

也可以使用 WithLifetime() 自定义

示例代码的执行结果

> Service: Service.IOrderService Lifetime: Scoped> Instance: Service.OrderService
> Service: Service.IUserService Lifetime: Scoped> Instance: Service.UserService
> Service: Service.IInvoiceRepository Lifetime: Singleton> Instance: Service.InvoiceRepository
复制代码

使用 Scrutor 注册装饰器

在 Demo 中新增一个 实现接口 IUserService 的 CachedUserService 类

    public class CachedUserService : IUserService    {         private readonly IUserService _repo;        private readonly ConcurrentDictionary<int, Task<User>> _dict;
public CachedUserService(IUserService repo) { _repo = repo; _dict = new ConcurrentDictionary<int, Task<User>>(); } // 先去 ConcurrentDictionary 键值对集合中查找缓存 public Task<User> GetUserByIdAsync(int userId) => _dict.GetOrAdd(userId, i => _repo.GetUserByIdAsync(userId)); }
复制代码


现在修改 Main 函数的代码为如下

    static async Task Main(string[] args)    {        var collection = new ServiceCollection();            collection.Scan(action => action.FromAssemblyOf<IScanAssemblyMarker>()            .AddClasses(@class => @class.Where(t => t.Name.EndsWith("Service")))            .UsingRegistrationStrategy(RegistrationStrategy.Throw)            .AsMatchingInterface()            .WithSingletonLifetime()            .AddClasses(@class => @class.Where(t => t.Name.EndsWith("Repository")))            .UsingRegistrationStrategy(RegistrationStrategy.Throw)            .AsMatchingInterface()            .WithScopedLifetime());    	// 给 已经注入的 IUserService 添加 Decorate        collection.Decorate<IUserService, CachedUserService>();            using (ServiceProvider root = collection.BuildServiceProvider())        using (IServiceScope scope = root.CreateScope())        {            IServiceProvider child = scope.ServiceProvider;            var blogRepository = child.GetRequiredService<IUserService>();            var blog1 = await blogRepository.GetUserByIdAsync(1);            var blog2 = await blogRepository.GetUserByIdAsync(1);        }	}
复制代码

在子容器中 连续运行 GetUserByIdAsync 方法时,第一次获取时还会经过装饰器时后由于键值对中没有缓存的值,将会继续调用 UserService.GetUserByIdAsync 去获取值,但是第二次调用时则直接拿取键值对中缓存的值

在 ASP.NET Core 中使用 Scrutor

示例

    // This method gets called by the runtime.     // Use this method to add services to the container.    public void ConfigureServices(IServiceCollection services)    {        services.AddControllers();    	// 扫描程序集的方法,并自动注册找到的类型        services.Scan(action => action.FromAssemblyOf<IScanAssemblyMarker>()            .AddClasses(@class => @class.Where(t => t.Name.EndsWith("Service")))            .UsingRegistrationStrategy(RegistrationStrategy.Throw)            .AsMatchingInterface()            .WithScopedLifetime()            .AddClasses(@class => @class.Where(t => t.Name.EndsWith("Repository")))            .UsingRegistrationStrategy(RegistrationStrategy.Throw)            .AsMatchingInterface()            .WithScopedLifetime());    	// 加上装饰器        services.Decorate<IUserService, CachedUserService>();    }
复制代码

参考文档

Using Scrutor to automatically register your services with the ASP.NET Core DI container

Adding decorated classes to the ASP.NET Core DI container using Scrutor

发布于: 刚刚阅读数: 9
用户头像

雄鹿 @

关注

心像开满花的树。 2019-01-04 加入

一名全栈开发工程师,热爱编程,对新技术充满好奇心,专注于使用ASP.NET Core和Angular进行Web应用的开发。

评论

发布
暂无评论
使用 Scrutor 来增强 ASP.NET Core 内置依赖注入框架_ASP.NET Core_雄鹿 @_InfoQ写作社区