c# – 依赖注入和第三方API – 带PI或Wrapper的扩展方法

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

我需要使用在几个页面中使用的常用功能来“扩展”CMS的api.
令人感兴趣的是这些常见功能具有多个依赖关系.
在这种情况下,使用静态扩展方法或通过创建新接口来扩展此功能更合适吗?

上下文

假设第三方有两个接口IContentLoader和IPageSecurity,它们与Page对象一起使用:

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
}

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

扩展方法

我可以使用Property Injection创建一个静态扩展方法:

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通常不如构造函数注入有益,应尽可能避免使用.

包装纸

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

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

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

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

摘要

在这种方法具有多个依赖关系的情况下,通过使用扩展方法来提升可发现性是否更好,并采用属性注入可能带来的任何脆弱性?或者构造注入是否如此有利于我应该创建一个包装类,代价是使该方法更难找到?

解决方法:

我会更倾向于包装类,但我会为它创建另一个接口.我会说它相似,所以开发人员可以找到它.

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

新的界面,但相同的起始名称,因此intellisense可以帮助开发人员.此接口还必须实现第三方接口.

我将更改您的AdvancedContentLoader类以实现此接口,并将对IContextLoader的所有调用链接到第三方实现,并仅处理它需要处理的特定方法.

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 Container,只需将IContentLoaderWithPageSecurity接口注册到该类,您仍然可以编写接口.

如果类的名称空间在using指令中,那么命名约定可以帮助开发人员使用intellisense找到它.

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

如果我不需要新的依赖关系,我只会倾向于扩展方法,而且可能只是已经存在的东西 – 否则你必须得到“智能”并进行属性注入或类似于ConditionalWeakTable来保持类的额外状态.

我同意Wiktor Zychla的说法,这开始成为人们的主观意见.

上一篇:如何从另一个对象的方法访问对象的属性,这是Python中的一个属性?


下一篇:c# – 如何确定MEF中不同目录的优先级?