Default-Ctor Extension and ParameterOverrides

Jun 28, 2010 at 11:40 AM

Hello,

I have a problem with the default behaviour of unity selection the ctor with the most parameters. Therefor I created a new UnityContainerExtension inclusive a new IConstructorSelectorPolicy class which returns the default ctor. So far so good.

But now I have to use the ParameterOverrides in the Resolve method to inject context specific values. With my current implementation the ParameterOverrides are completly ignored and the default ctor is always used for object creation.

The created code is hosted (because of its length) at: http://pastebin.com/EMC683uS

I hope someone of you can help me to solve my problem or can make tipps in what direction to go.

Jun 30, 2010 at 11:08 PM

Could you post an example of the code that does the resolve incorrectly?

Overrides only affect the parameter values, not which constructor is called, so unless the type was explicitly configured to call a different constructor, the override won't have any effect. But I'm not sure if that's the issue from your description.

Jul 1, 2010 at 6:44 AM
Edited Jul 1, 2010 at 6:44 AM

Incorrect resolve:

 

// The model to resolve
public class SafetyParameterGroupDefinition : ISafetyParameterGroupDefinition
{
    public SafetyParameterGroupDefinition() {}

    public SafetyParameterGroupDefinition(string name, bool isGlobal)
    {
        _name = name;
        _isGlobal = isGlobal;
    }
}

...

// The registration
public void RegisterTypes()
{
    DIContainerService.RegisterType<ISafetyParameterGroupDefinition, SafetyParameterGroupDefinition>();
}

...

// The resolve
ParameterOverrides overrides = new ParameterOverrides();
overrides.Add("name", "SS1");
overrides.Add("isGlobal", false);
ISafetyParameterGroupDefinition function = DIContainerService.Resolve<ISafetyParameterGroupDefinition>(overrides);

 

Yes, I know that the ParameterOverrides are not involved selecting the correct ctor. For selecting the specific ctor my created DefaultConstructorSelectorPolicy class is responsible for. But this class always selects the default ctor and does not consider the ParameterOverrides to select a different ctor where the overriden parameter matches.

Therefor the class DefaultConstructorSelectorPolicy must be extended or a separate extension should be created. But I am at my wits' end how to solve the problem to select the correct ctor for these parameters instead of the default ctor.

 

Jul 1, 2010 at 4:46 PM

The problem isn't in your extension, it's in your configuration.

When you called "RegisterType<ISafetyParameterGroupDefinition, SafeParameterGroupDefinition>()" you told the container to figure out which constructor to call. With your code in place, it did what you told it to and used the zero-argument constructor. If you want it to call the one that takes a string and a bool, you needed to:

RegisterType<ISafetyParameterGroupDefinition, SafetyParameterGroupDefinition>(
    new InjectionConstructor(typeof(string), typeof(bool)));

This tells the container you want to use that specific constructor.

 

Jul 2, 2010 at 7:54 AM

Thanks ctavares for your answer. But if I would use your code to register this type, unity will always use a ctor which matches the defined ctor parameters. In my use case unity should always use the default ctor, with the exception of giving a ParameterOverload, then unity should use a ctor which matches these parameters.

I hope the following code example demonstrates it a little better:

// use overloaded ctor -> new SafetyParameterGroupDefinition("SS1", false);
ParameterOverrides overrides = new ParameterOverrides();
overrides.Add("name", "SS1");
overrides.Add("isGlobal", false);
ISafetyParameterGroupDefinition overridenFunction = DIContainerService.Resolve<ISafetyParameterGroupDefinition>(overrides);

// use default ctor -> new SafetyParameterGroupDefinition();
ISafetyParameterGroupDefinition defaultFunction = DIContainerService.Resolve<ISafetyParameterGroupDefinition>();

Jul 2, 2010 at 8:23 PM

I had a feeling that's what you wanted. A constructor selector policy won't give you what you need.

Here's the issue - the build plan, the creation method we code gen under the hood, is only created the FIRST time you resolve a type. From then on, we re-use the build plan. The constructor called is "baked" into the build plan at creation time.

The reason we do this is performance. If we used reflection to decide which constructor to call every time, you'd see at least three orders of magnitude slower object creation time (I know, I timed it). The down side is as you see - the constructor is chosen once, and overrides don't affect which constructor is called.

You COULD do this, if you created a custom build plan policy, but be prepared for a bunch more work and the performance will most likely be terrible.

As an alternative, have you considered creating a separate SafetyParameterGroupDefinitionFactory? Get the factory out of the container, and then the factory could have explicit methods to new up the types you want (with references to the other stuff that the container supplies).