3

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.

1
  • 1
    Where does the values of these parameters/properties come from? A configuration file? Or from the user? Commented Nov 7, 2016 at 12:27

3 Answers 3

6

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);
}
Sign up to request clarification or add additional context in comments.

3 Comments

It's not really clear if these are runtime data. Maybe these are configuration data that come from the configuration file.
@YacoubMassad: The question states "because different controllers will use different values for the properties". This lead me to conclude that it is runtime data. But you are right, it could still be configuration data, and that would change the answer.
Sorry for the late response, I definitly ment runtime data! I will mark this as an answer cause the link steven gave us is an excellent guidance.
0

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.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.