使用动态构造函数注入的DI

本文关键字:DI 注入 构造函数 动态 | 更新日期: 2023-09-27 18:16:49

我有一个使用DI与构造函数属性的问题。我正在构建一个基于我的IPDFBuilderPDFBuilder

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...
}

但是,我不能在启动类(正在进行DI注册)中设置PDFBuilder的属性,因为不同的控制器将为PDFBuilder类的属性使用不同的值。1个简单的解决方案是只是使属性的setter公共,所以在一个动作方法我可以设置值,然后调用CreatePDF()。然而,这感觉不对。另一个简单的解决方案是删除类属性,并将PDFBuilder的3个属性作为方法属性传递给CreatePDF方法,如下所示:

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

但是现在我们假设我的PDFBuilder将有10个方法,它们都需要这3个属性。那么这不是正确的解,对吧?

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

使用动态构造函数注入的DI

您将运行时数据注入到组件的构造函数中,这是一件不好的事情。解决方案是将这些运行时值从构造函数移到CreatePDF方法中:

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

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

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

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

有两种方法:

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)
{
    ...
}

它将给你更多的灵活性初始化你的构建器,如果你决定改变他们的时间。你也可以自由地初始化这个配置——常量、配置、数据库等等。

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

选择最适合你的