25

Does Simple Injector allow you to pass parameters to constructor when you resolve? I'd like to know if both these frameworks do what Unity's ResolverOverride or DependencyOverride both do.

Liam
  • 27,717
  • 28
  • 128
  • 190
Ray
  • 4,679
  • 10
  • 46
  • 92
  • 2
    I think it's better to split your question in two separate questions (one per framework). This makes it easier for you to accept one single correct answer, and makes it easier in the future for anyone else to google this for the container in question. – Steven Feb 14 '14 at 23:12

4 Answers4

28

I suspect that this question is about passing primitive values to the constructor at the time the service is actually resolved.

Let's set up a simple test class:

public interface IFoo
{

}

public class Foo : IFoo
{
    public Foo(string value)
    {

    }
}

The Foo class takes a string argument that we would like to supply when resolving the IFoo service.

var container = new ServiceContainer();
container.Register<string, IFoo>((factory, value) => new Foo(value));
var firstFoo = container.GetInstance<string, IFoo>("SomeValue");
var secondFoo = container.GetInstance<string, IFoo>("AnotherValue");

If we want to be able to create new instances of the Foo class without using the container directly, we can simply inject a function delegate.

public interface IBar { }

public class Bar : IBar
{
    public Bar(Func<string, IFoo> fooFactory)
    {
        var firstFoo = fooFactory("SomeValue");
        var secondFoo = fooFactory("AnotherValue");
    }
}

The "composition root" now looks like this:

var container = new ServiceContainer();
container.Register<string, IFoo>((factory, value) => new Foo(value));
container.Register<IBar, Bar>();
var bar = container.GetInstance<IBar>();

If the question is about passing a "static" primitive value to the contructor, this is simply done by registering a factory delegate like this.

var container = new ServiceContainer();
container.Register<IFoo>((factory) => new Foo("SomeValue"));
var firstInstance = container.GetInstance<IFoo>();
var secondInstance = container.GetInstance<IFoo>();

The difference is that this approach does not let you pass a value at resolve time. The value is statically specified at registration time.

dmay
  • 1,340
  • 8
  • 23
seesharper
  • 3,249
  • 1
  • 19
  • 23
  • 12
    Everything is good. I just don't like the line container.Register((factory, value) => new Foo(value)); What if Foo has some other dependencies IService1,...,IServiceN which should be injected by a container? I would like just say, just use certain string for that particular parameter for the remaining parameters container should provide dependencies. How I can accomplish that? – user1325696 Jul 27 '15 at 06:33
  • 4
    What about `container.Register((factory) => new Foo(container.GetInstance(), "SomeValue"));`? – Marcello Mar 20 '16 at 09:45
21

Probably the easiest option with Simple Injector is to register with a delegate

[Test]
public void Test1()
{
    Container container = new Container();

    container.Register<IClassWithParameter>(() => new ClassWithParameter("SomeValue"));

    var result = container.GetInstance<IClassWithParameter>();
}

public interface IClassWithParameter { }

public class ClassWithParameter : IClassWithParameter
{
    public ClassWithParameter(string parameter)
    {
    }
}

An advanced option for injecting primitive dependencies is detailed here

anaximander
  • 7,083
  • 3
  • 44
  • 62
qujck
  • 14,388
  • 4
  • 45
  • 74
8

The above will all work if your constructor does not have any other dependencies (or you want to resolve these dependencies manually). If you have the scenario below though it falls down:

public class Test : ITest
{
   private IFoo _foo;
   public Test(string parameter, IFoo foo)
   {
      _foo = foo;
      ....
   }
}

Now you not only have to manually inject the string but also Foo. So now your not using dependancy injection at all (really). Also Simple Injector state:

Simple Injector does not allow injecting primitive types (such as integers and string) into constructors.

My reading of this is that they're saying "don't do this".

Extensibillity points

Another option here is to use "Extensibillity points" for this scenario.

To do this you need to abstract your hard coded elements from your injected elements:

public class Test : ITest
{
   private IFoo _foo;
   public Test(IFoo foo)
   {
      _foo = foo;
      ....
   }

  public void Init(string parameter)
  {

  }
}

You can now inject your dependanices and your hardcoded elements:

_container.Register<ITest, Test>();
_container.RegisterInitializer<Test>(instance => {instance.Init("MyValue");});

If you now add another dependancy, your injection will now work without you having to update the config, i.e. your code is nicely de-coupled still:

public class Test : ITest
{
   private IFoo _foo;
   private IBar _bar;
   public Test(IFoo foo, IBar bar)
   {
      _foo = foo;
      _bar = bar;
      ....
   }

  public void Init(string parameter)
  {

  }
}
Liam
  • 27,717
  • 28
  • 128
  • 190
2

In response to Liam's answer I would like to point out that there is a simpler way of doing this.

If you have the following situation:

public class Test : ITest
{
   private IFoo _foo;
   public Test(IFoo foo, string parameter)
   {
      _foo = foo;
      ....
   }
}

You could write your ioc configuration as below

_container.Register<IFoo, Foo>();
_container.Register<ITest>(
    () => new Test(
        _container.GetInstance<IFoo>(),
        "MyValue"
    )
);
  • That's possible, but it's not really DI-style. Imagine that your class `Test` suddenly needs another service - you go to its contructor and add another parameter `IBar bar`. @Liam solution will work without changes, yours will require editing container registration code. – Quercus Nov 20 '20 at 13:52
  • Hi Quercus, thanks for your feedback! You definitely have a point. – Jan Coppens Nov 23 '20 at 07:05
  • I think both solutions have something to them. If you take the DI-style solution, you have to manipulate your existing class by adding the Init method + you also depend on the DI executing the Init method after it has been constructed (Another developer not aware of your class could forget to use the Init method when constructing your class). I would prefer to use the parameter in the constructor to be sure that the parameter is really set before doing something with the class. – Jan Coppens Nov 23 '20 at 07:16
  • Autofac's delegate factories are a beautiful solution to this problem: https://autofac.readthedocs.io/en/latest/advanced/delegate-factories.html – gusmally supports Monica Nov 16 '21 at 14:09