前言

上一篇文章我们讲到了Scrutor第一个核心功能Scanning,本文讲解的是Scrutor第二个核心的功能Decoration 装饰器模式在依赖注入中的使用。

  • 装饰器模式允许您向现有服务类中添加新功能,而无需改变其结构
Install-Package Scrutor

本文的完整源代码在文末

Decoration 依赖注入代理模式

首先首先一个 获取 User 的服务

定义 User 类

public class User
{
public int Id { get; set; }
public string Name { get; set; } public int Age { get; set; } public string? Email { get; set; }
}

定义接口和实现

public interface IUserService
{
List<User> GetAllUsers();
} public class UserService : IUserService
{
public List<User> GetAllUsers()
{
Console.WriteLine("GetAllUsers方法被调用~");
List<User> users = [
new User(){
Id= 1,
Name="张三",
Age=18,
Email="zhangsan@163.com"
},
new User(){
Id= 2,
Name="李四",
Age=19,
Email="lisi@163.com"
},
];
return users!;
}
}

现在有了我们的获取全部用户的服务了,需求是在不破坏当前类的添加装饰器模式,为 GetAllUsers 接口添加缓存。

创建装饰器类

public class UserDecorationService(IUserService userService, IMemoryCache cache) : IUserService
{
public List<User> GetAllUsers()
{
Console.WriteLine("GetAllUsers代理方法被调用~");
return cache.GetOrCreate("allUser", cacheEntry =>
{
cacheEntry.SetAbsoluteExpiration(
TimeSpan.FromMinutes(5));
var allUsers = userService.GetAllUsers();
return allUsers ?? [];
})!;
}
}

DI 容器添加服务

 builder.Services.AddTransient<IUserService, UserService>();
builder.Services.AddMemoryCache();
builder.Services.Decorate<IUserService, UserDecorationService>();

创建接口测试一下

app.MapGet("/GetAllUsers", ([FromServices] IUserService userService) => userService.GetAllUsers()).WithSummary("获取全部用户接口");

调用第一次

GetAllUsers代理方法被调用~
GetAllUsers方法被调用~

第二次调用

GetAllUsers代理方法被调用~

可以看出第一次没缓存装饰器类和我们 UserService 都调用了,第二次因为只有了缓存所以只调用了装饰器类,可以看出我们的装饰器模式生效了。

依赖注入装饰器底层核心实现

    /// <summary>
/// Decorates all registered services using the specified <paramref name="strategy"/>.
/// </summary>
/// <param name="services">The services to add to.</param>
/// <param name="strategy">The strategy for decorating services.</param>
public static bool TryDecorate(this IServiceCollection services, DecorationStrategy strategy)
{
Preconditions.NotNull(services, nameof(services));
Preconditions.NotNull(strategy, nameof(strategy)); var decorated = false; for (var i = services.Count - 1; i >= 0; i--)
{
var serviceDescriptor = services[i]; if (IsDecorated(serviceDescriptor) || !strategy.CanDecorate(serviceDescriptor))
{
continue;
} var serviceKey = GetDecoratorKey(serviceDescriptor);
if (serviceKey is null)
{
return false;
} // Insert decorated
services.Add(serviceDescriptor.WithServiceKey(serviceKey)); // Replace decorator
services[i] = serviceDescriptor.WithImplementationFactory(strategy.CreateDecorator(serviceDescriptor.ServiceType, serviceKey)); decorated = true;
} return decorated;
}

这个代码是在 dotNet8 的环境下编译的,可以看出做了几件事:

第一 IServiceCollection 集合倒序遍历,找到符合条件的ServiceType

核心代码一

// Insert decorated
services.Add(serviceDescriptor.WithServiceKey(serviceKey));

将原先的ServiceDescription作为基础,添加了ServiceKey后再进行添加操作,新的服务描述符会被添加到服务集合的末尾,

核心代码二

 // Replace decorator
services[i] = serviceDescriptor.WithImplementationFactory(strategy.CreateDecorator(serviceDescriptor.ServiceType, serviceKey));

这一步是将原有的服务描述符替换为一个新的服务描述符,新的服务描述符使用装饰器工厂方法创建,实现了服务的装饰功能。

用的时候

app.MapGet("/GetAllUsers", ([FromServices] IUserService userService) => userService.GetAllUsers()).WithSummary("获取全部用户接口");

这样就可以获取到装饰器类提供服务,之前看到services.Add(serviceDescriptor.WithServiceKey(serviceKey));在代码的最后添加了一个服务,那 IOC 获取的时候肯定是从后面优先获取,这地方用了 dotNet8 的键控依赖注入(KeyedService),以 ServiceType 获取服务只会获取到我们提供的装饰器实例,这一手简直是神来之笔 。

最后

Scrutor的装饰器模式可以用于动态地给依赖注入的实例添加额外职责,实现动态增加和撤销功能,而无需改变原有对象结构。可以在不影响其他对象的情况下,以透明且动态的方式给对象添加新功能,实现系统的灵活扩展和维护。

本文完整源代码

.Net依赖注入神器Scrutor(下)的更多相关文章

  1. 【半小时大话.net依赖注入】(下)详解AutoFac+实战Mvc、Api以及.NET Core的依赖注入

    系列目录 上|理论基础+实战控制台程序实现AutoFac注入 下|详解AutoFac+实战Mvc.Api以及.NET Core的依赖注入 前言 本来计划是五篇文章的,每章发个半小时随便翻翻就能懂,但是 ...

  2. <Pro .NET MVC4> 三大工具之依赖注入神器——Ninject

    这篇内容是对<Pro .NET MVC4>一书中关于Ninject介绍的总结. Ninject是.NET MVC的一款开源的依赖注入工具. 使用场景:当MVC项目中使用了依赖注入技术来给程 ...

  3. 从零开始,搭建博客系统MVC5+EF6搭建框架(2),测试添加数据、集成Autofac依赖注入

    一.测试仓储层.业务层是否能实现对数据库表的操作 1.创建IsysUserInfoRepository接口来继承IBaseRepository父接口 namespace Wchl.WMBlog.IRe ...

  4. Spring的核心机制——依赖注入(Dependency Inject)

    Spring不仅提供对象,还提供对象的属性值,而不是由使用该对象的程序所提供的. Java应用是由一些相互协作的对象所组成的,在Spring中这种相互协作的关系就叫依赖关系. 如果A组件调用了B组件的 ...

  5. (五)Angularjs - 依赖注入

    如何找到API? AngularJS提供了一些功能的封装,但是当你试图通过全局对象angular去 访问这些功能时,却发现与以往遇到的库大不相同. 比如,AngularJS暴露了一个全局对象:angu ...

  6. AngularJS应用开发思维之3:依赖注入

    找不到的API? AngularJS提供了一些功能的封装,但是当你试图通过全局对象angular去 访问这些功能时,却发现与以往遇到的库大不相同. $http 比如,在jQuery中,我们知道它的AP ...

  7. 基于ABP模块组件与依赖注入组件的项目插件开发

    注意,阅读本文,需要先阅读以下两篇文章,并且对依赖注入有一定的基础. 模块系统:http://www.cnblogs.com/mienreal/p/4537522.html 依赖注入:http://w ...

  8. angular2的依赖注入

    更好阅读体验,请看原文 在读这篇文章之前,你要先了解一下什么是依赖注入,网上关于这个的解释很多,大家可以自行Google. 我们这一篇文章还是以QuickStart项目为基础,从头开始讲解怎么在Ang ...

  9. Spring的核心机制——依赖注入(Dependency Inject)

    Spring不仅提供对象,还提供对象的属性值,而不是由使用该对象的程序所提供的. Java应用是由一些相互协作的对象所组成的,在Spring中这种相互协作的关系就叫依赖关系. 如果A组件调用了B组件的 ...

  10. SignalR中的依赖注入

    什么是依赖注入? 如果你已经熟悉依赖注入可以跳过此节. 依赖注入 (DI) 模式下,对象并不为自身的依赖负责. 下边的例子是一个主动 DI. 假设你有个对象需要消息日志.你可能定义了一个日志接口: C ...

随机推荐

  1. .NET 云原生架构师训练营(模块二 基础巩固 EF Core 介绍)--学习笔记

    2.4.2 EF Core -- 介绍 ORM Repository 仓储 UnitOfWork 工作单元 DB Context 与 DB Set EF Core快速开始示例 ORM ORM:obje ...

  2. 如何获取oracle dbid

    1.查询v$database获得 由于DBID在控制文件和数据文件中都存在记录,所以如果能够mount数据库就可以查询v$database视图获得.  SQL> alter database m ...

  3. centos6关闭防火墙

    service iptable status     --查看防火墙状态 servcie iptables stop     --临时关闭防火墙 chkconfig iptables off      ...

  4. cf思维题

    1.B. Paranoid String 题意:操作一:01可以变成1,操作二:10可以变成0.给定一个串,判断字串经过若干次操作,能否长度变成1,统计数量. 思路:对01来说,1可以吃掉0,然后前边 ...

  5. 掌握云容器网络:何为ipvs

    本文分享自华为云社区<[理解云容器网络]2-基础篇-ipvs介绍>,作者: 可以交个朋友. IPVS简介 ipvs是工作在Linux内核态的4层负载均衡:和用户态的负载均衡软件(如ngin ...

  6. Direct2D 几何篇

    微软文档:Geometries overview 本篇通过官方文档学习,整理出来的demo,初始样本请先创建一个普通的desktop app. // Test_Direct2D_Brush.cpp : ...

  7. CSS之浮动Float

    前言 提到浮动,前端的小伙伴肯定都不陌生,但是随着弹性布局等等一些更好用的标准出来后,用在布局方面少了很多,当初我刚开始接触前端的时候,很习惯用浮动来给元素改变定位,当时还并不是很流行flexbox布 ...

  8. 项目实战:C#上位机+arduino下位机+控制点亮LED灯

    前言   当前比较流行的arduino开发,联动做一个Demo.   应用构架   上位机:C#上位机通过串口发送接收控制协议,来控制下位机:  下位机:arduino下位机主控,接受上位机串口协议控 ...

  9. ubuntu16.0.4设置mysql远程访问

    修改mysql的配置 1.先查看是不是已经是root用户了,不是的话切换到root用户 输入命令:sudo su 提示输入密码,这边输入你自己原来账户的密码即可 2.切换到root用户后,输入如下命令 ...

  10. 微信小程序:接手项目,修bug

    好家伙,   问题描述如下: 小程序主界面,选择快速上传会议记录 选择快速 其中,没有2022-2023第二学期,所以,新的会议记录无法上传 于是,我自愿修复这个bug 由于我们没有产品文档 我只能由 ...