C# 中的 Scrutor — 升级依赖注入的 3 个简单技巧

dc5adff68c654c6543518d004f37cb05.png

在组织依赖关系时,C# 中的依赖关系注入是救命稻草,尤其是在更复杂的 ASP.NET Core 应用程序中。如果你已经熟悉 IServiceCollection,或者只是想尽可能地接近已经提供的 DI 产品,那么 C# 中的 Scrutor 是一个很棒的增强功能。

在本文中,我将简要介绍 C# 中的依赖项注入和 Scitor。但从那里开始,我们将直接进入 3 个技巧,您可以将其与 Scrutor 一起使用,这些技巧可能对您的应用程序非常有帮助!

C# 中的 Scrutor 是什么?

Scrutor 是 C# 中功能强大的 NuGet 包,可增强依赖项注入。它简化了依赖项的注册和解析,使你能够更轻松地管理和组织代码。

依赖注入是一种常用的应用程序配置方法,可促进松耦合并提高可测试性和可维护性。它涉及将依赖项注入到类中,而不是直接在类中创建它们。这意味着创建依赖项的责任不归需要它的类所有,而是由调用堆栈中的某个人拥有。最终,这会将几乎所有的依赖项创建推到应用程序的入口点,使其变得笨拙。但是依赖注入框架有助于清理和组织所有这些逻辑。

Scrutor 通过提供一种简单直观的方法来注册和解析依赖关系,将这一概念向前推进了一步。使用 Scrutor,您不再需要手动逐个注册每个依赖项。相反,您可以使用约定和属性来自动执行该过程。

1 — 使用 Scrutor 进行注册和解决

为了说明如何在 C# 中使用 Scrutor 进行注册和解析,让我们考虑一个简单的场景。假设我们有一个应用程序,需要使用不同的数据存储库,例如 UserRepository 和 ProductRepostiory。

首先,我们需要在项目中安装 Scrutor NuGet 包。打开包管理器控制台并运行以下命令:

Install-Package Scrutor

接下来,我们需要定义我们的仓库及其相应的接口。这个例子基本上是空的,但我们稍后会提到这些:

public interface IUserRepository  
{  
    // Interface methods  
}  
  
public class UserRepository : IUserRepository   
{  
    // Implementation  
}  
  
public interface IProductRepository  
{  
    // Interface methods  
}  
  
public class ProductRepository : IProductRepository  
{  
    // Implementation  
}

现在,让我们使用 Scrutor 连接我们的依赖项。在启动代码(例如 ASP.NET Core 中的 ConfigureServices 方法中)中,添加以下代码:

services.Scan(scan => scan  
    .FromAssemblyOf<Startup>()  
    .AddClasses(classes => classes.AssignableToAny(  
        typeof(IUserRepository),  
        typeof(IProductRepository)))  
    .AsImplementedInterfaces()  
    .WithScopedLifetime());

此代码使用 Scrutor 中的 Scan 方法扫描包含 Startup 类的程序集。然后,它会筛选出可分配给 IUserRepository 或 IProductRepository 接口的类。最后,它映射了这些接口的实现,并将它们注册到各自的接口中。

现在,我们可以将这些依赖项注入到我们的类中。例如,假设我们有一个需要 IUserRepository 的 UserService 类:

public class UserService   
{  
    private readonly IUserRepository _userRepository;   
      
    public UserService(IUserRepository userRepository)   
    {   
        _userRepository = userRepository;  
    }  
  
    // Rest of the class implementation  
}

通过在构造函数中将 IUserRepository 声明为依赖项,Scrutor 和 IServiceCollection 将自动解析并注入 UserRepository 实例。

2 — 装饰器模式 — 服务日志记录示例

想象一下,你有一个服务接口和一个实现。您希望记录每个方法调用的入口和出口,而不会因日志记录问题而污染其业务逻辑。IServiceMyServiceMyService

首先,定义接口及其实现:IService

public interface IService  
{  
    void DoWork();  
}  
  
public class MyService : IService  
{  
    public void DoWork()  
    {  
        // Business logic here  
    }  
}

接下来,创建一个实现 .此类将包装实际服务,并围绕委托方法调用添加日志记录:LoggingDecoratorIService

public class LoggingDecorator : IService  
{  
    private readonly IService _decorated;  
    private readonly ILogger<LoggingDecorator> _logger;  
  
    public LoggingDecorator(IService decorated, ILogger<LoggingDecorator> logger)  
    {  
        _decorated = decorated;  
        _logger = logger;  
    }  
  
    public void DoWork()  
    {  
        _logger.LogInformation("Starting work.");  
        _decorated.DoWork();  
        _logger.LogInformation("Finished work.");  
    }  
}

现在,使用 Scrutor 在配置服务的任何位置注册服务及其装饰器:Startup.cs

public void ConfigureServices(IServiceCollection services)  
{  
    // Register the base service  
    services.AddScoped<IService, MyService>();  
  
    // Use Scrutor to apply the decorator  
    services.Decorate<IService, LoggingDecorator>();  
  
    // Make sure to register the ILogger or ILogger<T> dependencies if not already done  
  
}

此设置使用 Scrutor 将注册包装为 .解析时,DI 容器将提供 的实例,而 的实例又包装 的实例。此方法通过将日志记录逻辑保留在业务逻辑之外来实现关注点的分离。

3 — 服务实现的功能切换

当我们构建复杂的系统时,经常会出现一些类似功能标志的东西。这些允许我们根据配置采用不同的代码路径,而不必重新编译和部署整个应用程序。在某些情况下,这不仅仅是选择不同的代码路径,而是交换某些东西的整个实现!

让我们一起尝试一个例子!假设您有一个包含多个实现的接口:(一个新的实验性功能)和(当前的稳定功能)。您希望在运行时根据配置标志在这些实现之间切换。

首先,定义接口及其实现:IFeatureService

public interface IFeatureService  
{  
    void ExecuteFeature();  
}  
  
public class NewFeatureService : IFeatureService  
{  
    public void ExecuteFeature()  
    {  
        // New feature logic  
    }  
}  
  
public class StandardFeatureService : IFeatureService  
{  
    public void ExecuteFeature()  
    {  
        // Standard feature logic  
    }  
}

接下来,您需要一种方法来根据功能切换设置确定要使用的实现。这可以是 、数据库设置或任何其他配置源中的值。appsettings.json

现在,使用 Scrutor 根据功能切换动态注册相应的服务实现:

public void ConfigureServices(IServiceCollection services)  
{  
    // Assume GetFeatureToggleValue() retrieves the current feature toggle setting  
    var useNewFeature = GetFeatureToggleValue("UseNewFeature");  
  
    // Dynamically register the appropriate implementation based on the feature toggle  
    if (useNewFeature)  
    {  
        services.AddScoped<IFeatureService, NewFeatureService>();  
    }  
    else  
    {  
        services.AddScoped<IFeatureService, StandardFeatureService>();  
    }  
  
    // Optional: You could combine this with the previous example  
    // to use the decorator pattern too!  
    // services.Decorate\<IFeatureService, FeatureServiceProxy>();  
}  
  
private bool GetFeatureToggleValue(string featureName)  
{  
    // This method would typically check your  
    // configuration source to determine if the feature is enabled  
    // For simplicity, this is just a placeholder  
    return false; // or true based on actual configuration  
}

采取这样的方法可以帮助我们:

  • 灵活性:允许应用程序在功能实现之间切换,而无需重新部署。只需更改配置即可启用或禁用功能。

  • 在生产环境中测试:可以通过为这些用户打开功能,在具有有限用户集的生产环境中测试新功能。

  • 逐步推出:帮助逐步推出功能,以监控影响并确保在更广泛发布之前的稳定性。

  • 风险缓解:如果新功能出现问题,可以快速恢复到旧实现,从而最大限度地减少停机时间和对用户的影响。

在 C# 中总结 Scrutor

在本文中,我为您提供了 3 个在 C# 中使用 Scrutor 进行依赖注入的简单技巧。在简要概述了什么是依赖注入以及 Scrutor 如何适应它之后,我们直接进入了我们的示例。我们看到了程序集扫描,如何装饰依赖项,甚至如何考虑功能标记整个实现!

如果你喜欢我的文章,请给我一个赞!谢谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值