Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Passing parameter to ctor while having other parameters auto-resolved? #237

Closed
Shazwazza opened this issue Dec 20, 2015 · 10 comments
Closed

Comments

@Shazwazza
Copy link

Hi,

I have a (potentially strange) requirement in which I'd really like to be able to use the container to create a class that contains multiple ctor parameters while auto-resolving all parameters except also allow for specifying the value of potentially one or more at resolution time.

As an example let's say I have this class structure and I want to be able to specify the string value when I resolve IFoo, but I would like IBar1 and IBar2 to be auto-resolved:

public interface IFoo
{
}
public interface IBar1
{
}
public interface IBar2
{
}

public class Foo : IFoo
{
    public Foo(string value, IBar1 bar1, IBar2 bar2)
    {

    }
}

I have reviewed the answer on SO here on how to achieve this:
http://stackoverflow.com/questions/21785822/simple-injector-or-lightinject-pass-parameters-to-constructor?answertab=votes#tab-top

Which works, but this would mean that I would have to code the ctor for each instance I require whereas it'd be fantastic if the container could somehow still auto-resolve all of the parameters apart from the string.

Is there any way to achieve this?

@seesharper
Copy link
Owner

There is always a way to solve anything :)

So you have something like this

var container = new ServiceContainer();
container.Register<IBar1,Bar1>();
container.Register<IBar2, Bar2>();

container.Register<string, IFoo>(
    (factory, s) => new Foo(s, factory.GetInstance<IBar1>(), factory.GetInstance<IBar2>()));

var instance = container.GetInstance<string, IFoo>("SomeString");

What we could do here is scan for a certain pattern in the constructor of the target types and one such pattern could be that the first argument is a string and then we invoke the appropriate Register method such that when the GetInstance method is invoked with a string runtime argument, it would work as if you have explicitly registered the constructor.

What do you think? Would that be something that could work?

@Shazwazza
Copy link
Author

Haha, if there's a will, there's a way right :)

So what I was hoping could be done would be to register like:

var container = new ServiceContainer();
container.Register<IBar1,Bar1>();
container.Register<IBar2, Bar2>();
container.Register<IFoo,Foo>();

and then be able to resolve with:

var instance = container.GetInstance<string, IFoo>("SomeString");

And also be able to register the factory like:

//register the factory without having to specify the code to actual perform the ctor
container.Register<string, IFoo>();

That way you can specify a ctor parameter at runtime/resolution time but also have the container take care of wiring up the normal registered dependencies. I ask because I have a particular project that I'm converting over to use IoC and this ability would actually save me a ton of code and of course make refactoring of adding/removing ctor parameters much simpler since I wouldn't have to change the factory code each time they are changed. I know i can make it work without this functionality but writing less code is always a nice thing :)

It's worth noting that In my actual case the ctor parameter is an interface, but if this were possible in one way or another I'd presume it could support any parameter type and in theory parameter count.

@seesharper
Copy link
Owner

I will take a look at it tomorrow. I'm sure we can work something out :)

On Sunday, 20 December 2015, Shannon Deminick notifications@github.com
wrote:

Haha, if there's a will, there's a way right :)

So what I was hoping could be done would be to register like:

var container = new ServiceContainer();
container.Register<IBar1,Bar1>();
container.Register<IBar2, Bar2>();
container.Register<IFoo,Foo>();

and then be able to resolve with:

var instance = container.GetInstance<string, IFoo>("SomeString");

And also be able to register the factory like:

//register the factory without having to specify the code to actual perform the ctor
container.Register<string, IFoo>();

That way you can specify a ctor parameter at runtime/resolution time but
also have the container take care of wiring up the normal registered
dependencies. I ask because I have a particular project that I'm converting
over to use IoC and this ability would actually save me a ton of code and
of course make refactoring of adding/removing ctor parameters much simpler
since I wouldn't have to change the factory code each time they are
changed. I know i can make it work without this functionality but writing
less code is always a nice thing :)

It's worth noting that In my actual case the ctor parameter is an
interface, but if this were possible in one way or another I'd presume it
could support any parameter type and in theory parameter count.


Reply to this email directly or view it on GitHub
#237 (comment)
.

@seesharper
Copy link
Owner

I think I have found a solution for you.

If fact, LightInject "almost" supported this out of the box and only minor brain surgery was needed to make this happen.

Let's first take a look at the following unit test created for the sole purpose of this issue.

public void ShouldAutoResolveRemainingArguments()
{
    var container = CreateContainer();
    container.Register<IBar1,Bar1>();
    container.Register<IBar2, Bar2>();
    container.Register<IFoo, Foo>();               

    container.RegisterConstructorDependency((factory, info, runtimeArguments) => (string)(runtimeArguments[0]));                               
    var firstInstance = container.GetInstance<string,IFoo>("SomeValue");
    var secondInstance = container.GetInstance<string, IFoo>("AnotherValue");

    Assert.Equal("SomeValue", ((Foo)firstInstance).Value);
    Assert.Equal("AnotherValue", ((Foo)secondInstance).Value);
}

The RegisterConstructorDependency method has gotten an new overload that passes the runtime arguments supplied to the GetInstance method. This means that you can reason about the constructor parameter such as its type, declaring type and so on and then decide if you should fulfill the dependency using the given runtime argument. The rest of the dependencies are resolved as usual and you no longer need to be so explicit about the constructor dependencies even when using runtime arguments.

A new version of LightInject (4.0.3) has just been published to the official NuGet feed containing this new feature.

For reference here is the sample classes used in the test.

    public interface IFoo
    {
    }
    public interface IBar1
    {
    }
    public interface IBar2
    {
    }

    public class Bar1 : IBar1
    {

    }

    public class Bar2 : IBar2 
    {

    }

    public class Foo : IFoo
    {
        public string Value { get; private set; }
        public IBar1 Bar1 { get; private set; }
        public IBar2 Bar2 { get; private set; }

        public Foo(string value, IBar1 bar1, IBar2 bar2)
        {
            Value = value;
            Bar1 = bar1;
            Bar2 = bar2;
        }
    }

@Shazwazza
Copy link
Author

Amazing!! Yes that is wonderful. I did have a look at the RegisterConstructorDependency in hopes it would do what I needed ... and i guess now it does :)

You are a champion, will try this out asap, thanks!

@Shazwazza
Copy link
Author

Works great! That saves me a bunch of code :)

@seesharper
Copy link
Owner

That is great news. Merry Christmas :))

On Mon, Dec 21, 2015 at 4:30 PM, Shannon Deminick notifications@github.com
wrote:

Works great! That saves me a bunch of code :)


Reply to this email directly or view it on GitHub
#237 (comment)
.

@vkg-mca
Copy link

vkg-mca commented Aug 8, 2018

Guys...Thanks for lot of information but I cannot find ServiceContainer class in SimpleInjector version 4.3.0. I have a similar problem where I want to register a class with single constructor having three string parameters, but parameter values are not known until runtime. So my problem is
Step#1
private Container container = new Container();
container.RegisterSingleton<IEventHubConfiguration, EventHubConfiguration>();

Class has three parameters
Step#2
public class EventHubConfiguration : IEventHubConfiguration
{
public EventHubConfiguration(string key, string value, string account)
{
//Some code here
}
}

PROBBLEM=> In Step#1, I cannot register the class as it is asking for all three parameters to be passed. I can NOT pass the parameter values because these are NOT known yet. I can pass hardcoded values but that doesn't solve my problem

I have seen this example doing the same thing but my problem is different in 2 ways

  1. I cant find ServiceContainer in SimpleInjector
  2. I can only know the parameter values at runtime
    Please let me know if this issue is addressable

Regards
Vinod

@seesharper
Copy link
Owner

@vkg-mca If your issue is related to SimpleInjector you might want to consider opening an issue in the SimpleInjector repository 👍

https://github.com/simpleinjector

@vkg-mca
Copy link

vkg-mca commented Aug 10, 2018

Hi @seesharper , thanks for referring me to the right repository, thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants