Parametric Constructor Arguments

Mar 31, 2010 at 9:18 PM
Edited Jan 10, 2011 at 3:37 AM

I do not understand why the resolve method does not have a signature that takes constructor parameters, rather like this:

class MyParams {
  public string Name {get; get;}
  public int Height {get; set;}
  public int Weight {get; set;}
}

//some method contains...
IMyObject foo = mycontainer.Resolve<IMyObject>( new MyParams{Name="Bert", Height=20} );

Why isn't this capability provided?  I know I can use property injection after the object is already created, but that approach prevents me from following the "design by contract" strategy.  So I'm trying to explore the ways that I can supply parameters for construction of an object through Unity.

Best I can tell, there are two ways in Unity to achieve this - which preserve construction/initialization as a single step.  One is to register the "MyParams" object instance with the conainer, and let Unity perform constructor injection.  I suspect that approach is not thread-safe.  The other approach is to make a IMyObjectFactory that has a "Create" method which takes the MyParams instance.  I have the impression this second approach, using factories, is the common idiom with Unity.  Is that correct?   If yes, it still leaves me wondering why the mechanism I first proposed isn't being used?  Will it be in the future?  It certainly seems simpler. 

And by the way, I realize that the mechanism I'm proposing is in conflict with "ContainerLifeTimeManaged" registrations; I don't care.  If I try to resolve a lifetime managed object with constructor arguments, I'm happy to receive an exception.

I'm thinking the sample signature I first presented could even become type-safe or type-sensitive, perhaps through a "double dispatch" registration and resolve signatureset, rather like this:

DoubleDispatchRegisterType<TParam, TFrom, TTo>(...);
DoubleDispatchResolve<TParam, TFrom>(TParam param);

The above signatures could allow a different target to be made based on the parameter supplied. 

All this said, are my thoughts a reasonable usage story for Unity, or is there some reason why this mode of thinking is contrary to IOC container idiom?  The bottom line is that I don't understand why the ability to use non-default constructors is so restricted in Unity.

Apr 1, 2010 at 5:09 AM

The ability to pass parameters to constructors (or override other dependencies) was added several months ago in Unity 2.0.

I personally don't think it's a good idea, because passing parameters means you're coupling the code to a particular constructor on a concrete type. But enough people asked for it that we added it.

 

 

Apr 1, 2010 at 7:18 PM

One of the reasons I brought up this question, is because I cannot find a signature in unity - or documentation - that shows how to supply constructor parameters.  Could you please point me in the right direction, or give a tiny sample of code that uses this technique?

Apr 2, 2010 at 4:46 AM

The Unity 2.0 docs aren't live yet - we're still working to get them ready for our April 14th release. In the meantime, the best example would be in our unit tests - look at the ServiceOverrideFixture.cs file in the Unity tests for a bunch of variants on how it works.

 

Apr 9, 2010 at 4:52 PM

Thank you for pointing me to the example.  Question: What happens when the object to be resolved does not have a constructor that exactly matches the parameter overrides?  For example, if I have:

class Person : IPerson {
   public Person(string Name){...}
}

And I use the following code:

myContainer.Resolve<IPerson>(
                new ParameterOverrides {
                    { "name", “George Washington” },
                    { "age", 24} }.OnType<Person>()
                )

Clearly the "age" parameter override is spurious.  Does this cause an exception, or is the extra parameter silently ignored?

Looking at the above syntax also raises another question: is it really necessary to specify the "OnType<T>"?  I would like to specify parameter overrides that are used if they can be matched, but are ignored if they can't be matched. 

If that can't be done, perhaps I'm using the wrong syntax.  Is there some other mechanism in Unity that allows me to specify "ignorable extra parameters" within the context of a Resolve statement?

Apr 11, 2010 at 10:49 PM

Extra parameters are silently ignored.

OnType<T> is not necessary,  but if you don't use it then the override will match ANY constructor parameter with that name anywhere in the object graph. Sometimes that's what you want, sometimes it isn't, depends on what you're doing. With OnType, that restricts the override to that type's constructor only.

 

Apr 13, 2010 at 4:26 AM
Edited Apr 13, 2010 at 4:51 AM

That is very useful behavior!!! Thank you!

Now I need to know if OnType<> applies strictly to the identified type, or if it is polymorphic (that is, will it work on subclasses as well).  What I want to achieve with OnType<>() looks like this:

    ICar car = Car.Create("Ford",1990);

 Whereas Unity is invoked by the above code, because Person.Create is as shown below:

    public class Car : ICar
    {
        string make;
        int year;
        ILogger log; 

        //Preferably this would be protected, or at least internal
        //but yes, it would regrettably be public if necessary.
        //Indeed, at least I am now sure that RAII or "design by contract" are
        //design options for this "Car" class.

        public Car(string make, int year, ILogger log)
{
            this.make = make;
            this.year = year;
            this.log = log;
        }

        public static ICar Create(string make, int year)
        {
            //Assume Unity is in a singleton or some other static access point...

            return Container.Instance.Resolve<ICar>(
                new ParameterOverrides {{"make", make}, {"year", year}}.OnType<ICar>() );
        }
    }

Here we see that OnType<ICar>() is peculiar, because an interface can't have any constructors.  But my hope is that Unity will treat the "ICar" parameter polymorphically, and just look at the constructor(s) of whatever class happens to implement ICar.  In that case, all the magic simply works!

Is this the plan, or does OnType not work this way?

Apr 13, 2010 at 5:00 AM

Sorry, OnType doesn't work that way. But, if you just leave OnType out, then ANY constructor parameter named make and any constructor parameter named year in the object graph will get the overridden value, so it'll probably just work.

At this point I would encourage you to just try it out and see how it behaves.