c# - 依赖注入(inject)和第三方 API - 带 PI 的扩展方法或带 CI 的包装器

标签 c# design-patterns dependency-injection extension-methods composition

我正在开发一个位于第 3 方 CMS 之上的 C# 项目。该团队正在利用依赖注入(inject)来促进类之间的松散耦合。

我需要使用在多个页面中使用的通用函数来“扩展”CMS 的 api。
有趣的是这些常用函数具有多个依赖项。 在这种情况下,使用静态扩展方法或通过创建新接口(interface)来扩展此功能更合适吗?

上下文

假设第 3 方有两个接口(interface) IContentLoaderIPageSecurityPage 对象一起工作:

namespace 3rdParty.Api
{
    public class Page{}

    public interface IContentLoader{
        T LoadItem<T>(Guid id) where T : Page;
    }

    public interface IPageSecurity
    {
        bool CurrentUserCanReadPage(Page p);   
    }
}

我想写一个通用的方法,比如:

public IEnumerable<T> LoadAllChildPagesTheUserCanRead(Guid id) where T:Page
{
    //load page, recursively collect children, then
    //filter on permissions
}

(我承认这个例子有点陈词滥调)

扩展方法

我可以使用属性注入(inject)创建静态扩展方法:

public static class IContentLoaderExtensions
{
   public static Injected<IPageSecurity> PageSecurity {get;set;}

   public static IEnumerable<T> LoadAllChildItems(
      this IContentLoader contentLoader, Guid id){}
}

此方法很容易被发现,我们经常使用 IContentLoader,因此团队成员更容易找到它。但是,我有 read that Property Injection通常不如构造函数注入(inject)有益,应尽可能避免。

包装器

另一方面,我可以创建一个包装器:

public class AdvancedContentLoader
{        
     public AdvancedContentLoader(IContentLoader c, IPageSecurity p){
       //save to backing fields
     }

     IEnumerable<T> LoadAllChildItems(Guid id){}  
}

这种方法允许构造函数注入(inject),这避免了属性注入(inject)的潜在危险,但使该方法不易被发现。消费者需要知道依赖 AdvancedContentLoader 而不是使用他们习惯的 IContentLoader

总结

在这种情况下,一个方法具有多个依赖项,是不是通过使用扩展方法来提高可发现性并采取使用属性注入(inject)可能带来的任何脆弱性更好?还是构造注入(inject)如此有利以至于我应该以使方法更难找到为代价创建一个包装类?

最佳答案

我会更倾向于包装器类,但我会为它创建另一个接口(interface)。我会将其命名为类似的名称,以便开发人员可以找到它。

public interface IContentLoaderWithPageSecurity : IContentLoader
{
    IEnumerable<T> LoadAllChildItems<T>(IContentLoader contentLoader, Guid id) { }
}

新界面但起始名称相同,因此智能感知可以帮助开发人员。此接口(interface)还必须实现第 3 方接口(interface)。

我会更改您的 AdvancedContentLoader 类以实现此接口(interface)并将对 IContextLoader 的所有调用链接到第 3 方实现并仅处理它需要处理的特定方法。

public class AdvancedContentLoader : IContentLoaderWithPageSecurity 
{
    private readonly IContentLoader _internalContentLoader;
    private IPageSecurity _pageSecurity;

    public AdvancedContentLoader(IContentLoader contentLoader, IPageSecurity pageSecurity)
    {
        _internalContentLoader = contentLoader;
        _pageSecurity = pageSecurity;
    }

    // Chain calls on the interface to internal content loader
    public T LoadItem<T>(Guid id) where T : Page
    {
        return _internalContentLoader.LoadItem<T>(id);
    }

    public IEnumerable<T> LoadAllChildItems<T>(IContentLoader contentLoader, Guid id)
    {
        // do whatever you need to do here
        yield break;
    }
}

这样做的好处是,如果您使用的是 DI 容器,您只需将 IContentLoaderWithPageSecurity 接口(interface)注册到该类,而您仍在为接口(interface)编码。

如果类的 namespace 在 using 指令中,命名约定可以帮助开发人员使用智能感知找到它。

新接口(interface)实现了旧接口(interface),因此需要 IContentLoader 的现有代码库仍然可以将 IContentLoaderWithPageSecurity 传递给这些方法。

如果我不需要新的依赖项并且可以只是已经存在的东西,我只会倾向于扩展方法 - 否则你必须变得“聪明”并进行属性注入(inject)或类似 ConditionalWeakTable 的东西来保持额外的状态类。

我同意 Wiktor Zychla 的观点,即这开始成为人们的主观意见。

关于c# - 依赖注入(inject)和第三方 API - 带 PI 的扩展方法或带 CI 的包装器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26473091/

相关文章:

c# - API架构

asp.net-core - 为什么 Giraffe/AspNetCore + SignalR 依赖注入(inject)无法解析 MailboxProcessor 单例?

angular - ActivatedRoute 在服务中不起作用

javascript - 如何在 JS 或 C# 中使用输入按钮更新 html 表格行的 SQL 数据?

c# - 如何通过Mono Cecil获取字段的初始值?

C# BinaryWriter 长度前缀 - UTF7 编码

javascript - 使用 Angular JS 将常量注入(inject)其他模块配置

c# - 边界表示数据结构

python - 状态模式 - Django 模型

java - 简单的java消息调度系统