How to provide a ParameterOverride when Constructor parameter is of System.Type

Dec 3, 2012 at 6:53 AM

I'm attempting to register a class whose constructor has a single parameter of System.Type, e.g.

public class Something { public Something(Type x) { ...} ...}

My latest attempt to register the class looks like

Container.RegisterType(typeof(Something), "Default",

new InjectionConstructor(new InjectionParameter<Type>(typeof(object))));

And my latest attempt to resolve it

 

 

 

var y = Container.Resolve(typeof(Something), "Default",new ResolverOverride[] { new ParameterOverride("x", impl) });  // where impl is a Type

Regardless of the permutations I've tried, the result is an exception "Unable to case "impl" to System.Type."

Short of debugging through Unity, I'm at a loss on the formulation of the correct syntax. Any help appreciated.

 

Dec 5, 2012 at 7:04 PM

I remember a similar issue being raised on the forums ( but for the life of me cannot find it ).

Unity has a bit of magic that if you pass a Type as a parameter it assume that you want to create an instance of the type, not the actual type itself.

 

Hence your error... the instance of the Type referred to by impl can't be cast to System.Type

 

If someone can find the forum post then it might also contain the resolution, but I don't think you can work around this behaviour.

 

Cheers...

Dec 5, 2012 at 7:26 PM
Edited Dec 5, 2012 at 7:28 PM

Based on the posted code you don't actually need to specify the InjectionConstructor (or even call RegisterType).  However, I'll assume that this is just a simplified example and you actually want to differentiate between multiple constructors.

Here is the code to do what you want:

    public class Something
    {
        public Something(Type x)
        {
            Console.WriteLine("Injected type: " + x.Name);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IUnityContainer container = new UnityContainer();

            container.RegisterType(typeof(Something), "Default",
                                        new InjectionConstructor(typeof(Type)));

            Type impl = typeof(string);
            var y = container.Resolve(typeof(Something), "Default", 
                new DependencyOverride(typeof(Type), 
                        new InjectionParameter(impl)));
        }
    }

Resolving Objects by Using Overrides does a good job discussing this topic.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Dec 5, 2012 at 8:43 PM

Thank you. While I've read the overview (multiple times), I'm still getting Unity under my belt and the subtlety between ParameterOverride and DependencyOverride I still find confusing.

Dec 6, 2012 at 6:15 PM
Edited Jan 11, 2013 at 12:47 AM

It is a bit arcane.  Your original approach with ParameterOverride is usually sufficient but because of the Type parameter it causes issues as per @rslaney's post.

Basically, when the second argument to ParameterOverride is a Type then Unity tries to create an instance of the type.  So, let's change the example so that the constructor accepts a string.

 

    public class Something
    {
        public Something(string x)
        {
            Console.WriteLine("Injected string: " + x);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IUnityContainer container = new UnityContainer();

            // Register default string instance 
            string stringValue = "The quick brown fox...";
            container.RegisterInstance<string>(stringValue);
            
            Type impl = typeof(string);

            // impl which is System.Type of type System.String is now resolved from the container
            // Outputs: The quick brown fox...
            var x = container.Resolve<Something>(new ParameterOverride("x", impl));

            // But if we pass in a string we *can* override the value
            // Outputs: ...jumps over the lazy dog.
            var y = container.Resolve<Something>(new ParameterOverride("x", "...jumps over the lazy dog."));
        }
    }

 

Now when we pass in the Type (impl variable) Unity will resolve the string type and inject that into the constructor.  If we hadn't registered the string Unity would have thrown because it would not have been able to instantiate a System.String.

Hope that helps clarify.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

 
Dec 11, 2012 at 2:37 PM
Your samples worked well. I've encountered another peculiarity for which I've found no circumvention. If a class has a constructor with a single parameter of System.Object and another with a single parameter of System.Type, I can configure both, but again when I try to resolve, Unity faults with a 'ResolutionFailedException'.

public interface ISomeEntity

{

string Info { get; }

}

public class X : ISomeEntity

{

private string Id = "HiDave";

public string Info { get { return Id; } }

$B!!(B

public X(object info)

{

Id = ((int)info).ToString();

Console.WriteLine("Object constructor: " + Info);

}

//public X(Type info)

//{

// Id = info.Name;

// Console.WriteLine("Type Constructor: " + Info);

//}

}

internal class MyTest

{

public void DoIt()

{

IUnityContainer container = new UnityContainer();

$B!!(B

container.RegisterType<ISomeEntity, X>("asObj",

new InjectionConstructor(typeof(object)));

//container.RegisterType<ISomeEntity, X>("asType",

// new InjectionConstructor(typeof(Type)));

var u = container.Resolve<ISomeEntity>("asObj",

new DependencyOverride(typeof(object),

new InjectionParameter(492)));

//Type xType = typeof(X);

//var v = container.Resolve<ISomeEntity>("asType",

// new DependencyOverride(typeof(Type), new InjectionParameter(xType)));

}

}

In this sample you can Resolve successfully with either constructor/registration/resolve triplet, but if you add both constructors and both registrations, then the second resolve will fault.

Is this expected?

David John Killian
Chief Software Product Architect, System and Product Integration Services, ITSG
Office: (919) 556-8171
Mobile: (919) 608-1509
E-mail: dave.killian@us.fujitsu.com
Dec 12, 2012 at 5:11 AM
Edited Jan 11, 2013 at 12:52 AM

You are poking around in the corners here.  :)

I've "corrected" the code for you:

    public interface ISomeEntity
    {

        string Info { get; }

    }

    public class X : ISomeEntity
    {

        private string Id = "HiDave";

        public string Info { get { return Id; } }

        public X(Type info)
        {

            Id = info.Name;

            Console.WriteLine("Type Constructor: " + Info);
        }

        public X(object info)
        {
            Id = ((int)info).ToString();

            Console.WriteLine("Object constructor: " + Info);
        }
    }

    internal class MyTest
    {

        public void DoIt()
        {
            X x = new X(typeof(X));
            
            IUnityContainer container = new UnityContainer();


            container.RegisterType<ISomeEntity, X>("asObj",

            new InjectionConstructor(typeof(object)));

            container.RegisterType<ISomeEntity, X>("asType",

             new InjectionConstructor(typeof(Type)));

            var u = container.Resolve<ISomeEntity>("asObj",

            new DependencyOverride(typeof(object),

            new InjectionParameter(492)));

            Type xType = typeof(X);

            var v = container.Resolve<ISomeEntity>("asType",

             new DependencyOverride(typeof(Type), new InjectionParameter(xType)));

        }

    }

Do you see the difference?  The only change (should be) in the order of the constructors.  So, in this case order matters.  In the original code the incorrect constructor was being selected and Unity was trying (and failing) to instantiate a Type instance.

The issue is that even though an InjectionConstructor is specified the correct constructor must still be selected.  Currently the matching logic runs through all of the constructors in order and then checks (assuming the number of parameters are the same) if the the constructor parameter type IsAssignableFrom() the InjectionConstructor type.  So if you use object as the first constructor then this will always match (given the same number of arguments).

So there are a few workarounds (roughly in reverse order of preference, IMO) : 

  • Change the order of the constructors in the class so that the object constructor is last
  • Add an extra parameter(s) to the object constructor so it does not get selected based on the number of parameters
  • Try not to use object as a constructor parameter type especially in tandem with overloads

I'm not sure if the Unity behavior is by design or not.  A fix could be to first check the explicit type matches before checking IsAssignableFrom (this is how open generics are checked).  That would be a bit of a performance hit (and maybe there are some gotchas I'm not seeing in that approach). 

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Dec 12, 2012 at 10:22 AM
Thanks again for the quick response. I had already implemented the 'extra (unused) constructor parameter' workaround by the time I got this. Since I'm constrained at this time from modifying the target class (the one with the object constructor), I'll probably stay with that solution; order dependent constructors sounds like a maintenance nightmare.
David John Killian
Chief Software Product Architect, System and Product Integration Services, ITSG
Office: (919) 556-8171
Mobile: (919) 608-1509
E-mail: dave.killian@us.fujitsu.com
Dec 12, 2012 at 1:04 PM

> order dependent constructors sounds like a maintenance nightmare.

Yes, I agree.  But it's worse than that: it depends on the implementation of Type.GetConstructors().  In the case we were looking at it was returning the constructors in the order they were defined in the class (but what about other scenarios such as partial classes etc.?).  Actually, the Type.GetConstructors Method explicitly calls this out:

The GetConstructors method does not return constructors in a particular order, such as declaration order. Your code must not depend on the order in which constructors are returned, because that order varies.

So let's remove that as a viable option.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com