DI 使用动态构造函数注入

DI using dynamic constructor injection

我在使用带有构造函数属性的 DI 时遇到问题。我正在根据我的 IPDFBuilder.

构建一个 PDFBuilder
public interface IPDFBuilder
{
    string templatefilepath { get; }
    string templatefilename { get; }
    Dictionary<string, string> dict { get; }    
    void CreatePDF();
}

public class PDFBuilder : IPDFBuilder
{
    public string templatefilename { get; private set; }
    public string templatefilepath { get; private set; }
    public Dictionary<string, string> dict { get; private set; }

    public PDFBuilder(string templatefilename, string templatefilepath, Dictionary<string, string> dict)
    {
        this.templatefilename = templatefilename;
        this.templatefilepath = templatefilepath;
        this.dict = dict;
    }

    public void CreatePDF() {
        //Do something
    }
}

这个PDFBuilder可以并且将会在多个控制器中使用,例如:

public class KeuringController : Controller
{
    private IPDFBuilder _PDFBuilder;
    public KeuringController(IPDFBuilder pdfBuilder)
    {
        _PDFBuilder = pdfBuilder;
    }
    //Action methods that use `PDFBuilder` below...
}

但是,我无法在启动 class(DI 注册已完成)中设置 PDFBuilder 的属性,因为不同的控制器将对 [= 的属性使用不同的值13=] class。一个简单的解决方案是只创建属性 public 的设置器,因此在操作方法中我可以设置值然后调用 CreatePDF()。然而,这感觉不对。另一个简单的解决方案是删除 class 属性,并将 PDFBuilder 的 3 个属性作为方法属性传递给 CreatePDF 方法,如下所示:

public void CreatePDF(string templatefilename, string templatefilepath, Dictionary<string, string> dict) {
        //Do something
    }

但是现在假设我的 PDFBuilder 有 10 个方法都需要这 3 个属性。那么这不是正确的解决方案吗?

那么正确的解决方案是什么?我在不同的 classes/interfaces 实现中多次遇到这个问题,希望在这些情况下的设计方面得到一些帮助。

您正在将运行时数据注入组件的构造函数,即 bad thing。解决方案是将这些运行时值从构造函数中移出到 CreatePDF 方法中:

public interface IPDFBuilder
{
    void CreatePDF(string templatefilepath, string templatefilename,
        Dictionary<string, string> dict);
}

您可以子类化(或原型,取决于您的要求)不同种类的 PDFBuilder 并将它们注入相应的 类。

我不知道你使用的是什么 DI 框架,但我很确定有一个选项可以告诉框架你想在特定的 类.[=10 中注入什么样的依赖关系=]

编辑:请记住:此解决方案不适用于运行时已知的值。

有两种方法可以满足您的需求:

1) Create factory for builder.

2) Create configurator for builder.

当你创建工厂时,你基本上指定了对象的创建方式,因此可以自由地将你想要的一切设置到不同构建器的不同实现中:

public inteface IPDFBuilderFactory
{
    IPDFBuilder Create();
}

您将需要传递所有依赖项 - 这是一个缺点。我个人不喜欢这种方法。

另一种方法是创建这样的配置:

public interface IPDFConfiguration
{
    string templatefilename {get;}
    string templatefilepath {get;}
    Dictionary<string, string> dict {get;}
}

并将其作为参数传递给构造函数:

public PDFBuilder(IPDFConfiguration configuration)
{
    ...
}

如果您决定在一段时间内更改它们,它将为您在初始化构建器时提供更多的灵活性。您也可以自由地初始化此配置 - 常量、配置、数据库等。

其中一个缺点 - 随着时间的推移,您的配置可能会变得非常笨拙,如果不进行重构,它将成为其他人的黑洞,所以要小心。

选择最适合你的。