Combining DI with constructor parameters?

C#NinjectDependency Management

C# Problem Overview


How do I combine constructor injection with "manual" constructor parameters? ie.

public class SomeObject
{
    public SomeObject(IService service, float someValue)
    {
    }
}

Where IService should be resolved/injected by my DI container, and someValue should be specified. How do I mix the two?

C# Solutions


Solution 1 - C#

Such constructs should be avoided whenever possible. Therefore, ask yourself: is this parameter really required as constructor argument? Or can SomeObject be replaced by a stateless one which is reused by everyone that depends on it by passing the parameter to the method you execute on the object?

e.g. Instead of

public class SomeObject
{
    private float someValue
    public SomeObject(IService service, float someValue)
    {
        this.someValue = someValue
    }

    public float Do(float x)
    {
        return this.Service.Get(this.someValue) * x;
    }
}

use

public class SomeObject
{
    public SomeObject(IService service)
    {
    }

    public float Do(float x, float someValue)
    {
        return this.Service.Get(someValue) * x;
    }
}

If it is required go for a factory:

public interface ISomeObjectFactory
{
    ISomeObject CreateSomeObject(float someValue);
}

public class SomeObjectFactory : ISomeObjectFactory
{
    private IKernel kernel;
    public SomeObjectFactory(IKernel kernel) 
    {
        this.Kernel = kernel;
    }

    public ISomeObject Create(float someValue)
    {
        return this.kernel.Get<ISomeObject>(WithConstructorArgument("someValue", someValue);
    }
}

Preview: Ninject 2.4 won't require the implementation anymore but allow

kernel.Bind<ISomeObjectFactory>().ToFactory();  // or maybe .AsFactory();

Solution 2 - C#

You really shouldn't try to use D.I. for this. You could come up with all types of wacky solutions, but they may not make sense down the road.

Our approach is to create a factory via D.I., and the factory's Create method would then build itself out using the passed in D.I. container. We don't have to use this pattern often, but when we do it actually makes the product much cleaner (since it makes our dependency graphs smaller).

Solution 3 - C#

Another approach - initialization in two steps (not ninject related, any DI framework):

public class SomeObject
{
    private readonly IService _service;

    public SomeObject(IService service)
    {
        // constructor only captures dependencies
        _service = service;
    }

    public SomeObject Load(float someValue)
    {
        // real initialization goes here
        // ....

        // you can make this method return no value
        // but this makes it more convienient to use
        return this;
    }
}

and usage:

public static class TestClass
{
    public static void TestMethod(IService service)
    {
        //var someObject = new SomeObject(service, 5f);
        var someObject = new SomeObject(service).Load(5f);
    }
}

Solution 4 - C#

I would probably use a naive solution to this. If you know the value of someValue when you need it I would remove it from the constructor and add a property to your object so you can set someValue. This way you can get your object from your container and then set the value when you have the object.

My other suggestion is that you instead of accessing it directly you create a factory that you can use to create such object. Then you register the factory in your container and use the factory to create your instance. Something like this:

public class SomeObjectFactory : ISomeObjectFactory
{
    private IYourService _service;
    public SomeObjectFactory(IYourService service) 
    {
        _service = service;
    }

    public ISomeObject Create(float someValue)
    {
        return new SomeObject(_service, someValue);
    }
}

you could try a pattern like that.

UPDATE: Updated the code to reflect improvement comments.

Solution 5 - C#

I am not sure this is a good practice, but it could be solved in a different way, If you create an interface for the parameters, then a class that implements the interface with the values that you need (or fetch from somewhere). That way DI works with those parameters as well.

interface ISomeParameters
{
  public float SomeValue { get; set; }
}

class SomeParameters : ISomeParameters
{
 public float SomeValue{ get; set; } = 42.0;
}

services.AddSingleton(ISomeParameters, SomeParameters)

public MyService(IService service, ISomeParameters someParameters)
{
  someParameters.SomeValue
 ...

Solution 6 - C#

If 'somevalue' is always constant then you can think of using InjectionParameters while you are register your type with the container as it explained in the below post

See Here

but if that is not true, than there is no way to sepcify a parameter value while resolving a instance , you may think of moving the 'someValue' from the constructor and make it a property of the class.

Solution 7 - C#

In NInject, which you have tagged this with, you inject an automatically-generated Factory in the form of a Func<parameters you wish to feed in,T>, using the FuncModule as described in this post.

This approach is also available in autofac for one.

The various Factory method approaches are covered in the answers to this question.

EDIT: NB While this may be entertaining, please use @Remo Gloor's solution (and critically the advice re avoiding a solution of this nature)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGeorge RView Question on Stackoverflow
Solution 1 - C#Remo GloorView Answer on Stackoverflow
Solution 2 - C#Beep beepView Answer on Stackoverflow
Solution 3 - C#ArekView Answer on Stackoverflow
Solution 4 - C#Tomas JanssonView Answer on Stackoverflow
Solution 5 - C#AndersKView Answer on Stackoverflow
Solution 6 - C#TalentTunerView Answer on Stackoverflow
Solution 7 - C#Ruben BartelinkView Answer on Stackoverflow