简体   繁体   中英

DI using dynamic constructor injection

I have a problem using DI with constructor properties. I am building a PDFBuilder based on my 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
    }
}

This PDFBuilder can and will be used in multiple controllers, for example:

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

However, I can't set the properties of PDFBuilder in the startup class (where DI registration is beeing done) because different controllers will use different values for the properties of the PDFBuilder class. 1 simple solution would be to just make the setters of the properties public so in an action method I can set the values and then call CreatePDF() . However this doesn't feel right. Another simple solution would be to remove the class properties and just pass the 3 properties of PDFBuilder as method properties to the CreatePDF method like this:

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

But now let's say that my PDFBuilder whould have 10 methods which all need these 3 properties. Then this is not the correct solution right?

What would be the correct solution then? I have encountered this problem multiple times with different classes/interfaces implementations and would like to have some help with designing in these situations.

You are injecting runtime data into your component's constructor, which is a bad thing . The solution is to move those runtime values out of the constructor into the CreatePDF method:

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

You could subclass (or prototype, depending on your requirements) different kinds of PDFBuilders and inject them into the according classes.

I don't know what DI Framework you're using, but i'm pretty sure theres an option to tell the framework what kind of dependency you want to inject in specific classes.

Edit: Keep in mind: This solution is not applicable for values known at runtime.

There is two ways of doing what you want:

1) Create factory for builder.

2) Create configurator for builder.

When you create factory, you basically specify how object is created and so can freely set everything you want into different implementations for different builders:

public inteface IPDFBuilderFactory
{
    IPDFBuilder Create();
}

You will need to pass all dependencies - this is a drawback. I personally don't like this method.

Another way, is to create configuration like this:

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

and pass it as argument to constructor:

public PDFBuilder(IPDFConfiguration configuration)
{
    ...
}

It will give you more flexebility in initialization of your builders if you deside to change them some time. Also you can freely initialize this configuration - constants, configs, databases, etc.

One of drawbacks - your configuration can become very clumsy over time and without refactoring it will be black hole for others, so be carefull.

Choose what fits you the best.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM