DI使用动态构造函数注入

时间:2016-11-07 10:18:50

标签: c# dependency-injection constructor-injection

使用DI和构造函数属性时遇到问题。我正根据PDFBuilder建立IPDFBuilder

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

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

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

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

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

3 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:0)

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

我不知道您正在使用什么DI框架,但我非常确定可以告诉框架您希望在特定类中注入哪种依赖项。

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

答案 2 :(得分:0)

有两种方法可以做你想做的事:

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

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

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

选择最适合您的方式。