Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolve instance with multiple constructors using unity

I'd like to create an instance of a class using unity where the class has two constructors with the same number of parameters.

Here is the instantiation:

_unityContainer.Resolve<IGradeType>(new ParameterOverride("gradeTypeStringFromXmlFile", gradeTypeStringFromXmlFile));

And here are the constructors:

    public GradeType(string gradeTypeStringFromXmlFile)
    {
        _gradeTypeStringFromXmlFile = gradeTypeStringFromXmlFile;
    }

    public GradeType(Enum.GradeType gradeType)
    {
        _gradeType = gradeType;
    }

If I try to do this I get an exception saying The type GradeType has multiple constructors of length 1. Unable to disambiguate.

I can set the attribute [InjectionConstructor] over one constructor to make it work with one, but then I can't create an instance with unity using the other constructor.

Is it some way to have multiple constructors with equal number of parameters and still use unity to create the instances?

like image 699
FatAlbert Avatar asked Jan 13 '12 15:01

FatAlbert


2 Answers

Yes it's possible to tell Unity which constructor should it use, but you can only do this when you register your type with InjectionConstructor. If you want to use both constructor it's even complicated because you have to name your registrations and use that name when resolving.

Sample built with Unity version 2.1.505:

var continer = new UnityContainer();

continer.RegisterType<IGradeType, GradeType>("stringConstructor", 
    new InjectionConstructor(typeof(string)));

continer.RegisterType<IGradeType, GradeType>("enumConstructor",
    new InjectionConstructor(typeof(EnumGradeType)));

IGradeType stringGradeType = continer.Resolve<IGradeType>("stringContructor" , 
    new DependencyOverride(typeof(string), "some string"));

IGradeType enumGradeType = continer.Resolve<IGradeType>("enumConstructor", 
    new DependencyOverride(typeof(EnumGradeType), EnumGradeType.Value));
like image 125
nemesv Avatar answered Oct 25 '22 01:10

nemesv


An alternative option using Reflection and following the Strategy Pattern.

1) Create a base class for the constructors' arguments

public abstract class ConstructorArgs
{
}

2) Create a sequence of different concrete arguments classes:

public class StringArg : ConstructorArgs
{
    public string _gradeTypeStringFromXmlFile { get; set; }

    public StringArg (string gradeTypeStringFromXmlFile)
    {
        this._gradeTypeStringFromXmlFile = gradeTypeStringFromXmlFile ;
    }
}

public class EnumArg : ConstructorArgs
{
    public Enum.GradeType _gradeType { get; set; }

    public EnumArg (Enum.GradeType gradeType)
    {
        this._gradeType = gradeType ;
    }
}

3) Now in your GradeType class create the methods required for the Reflection. The ParseArguments scans the args for properties and for each one that it finds, it copies its value to the respective property of the GradeType using the SetProperty. Since it uses the property name for the matching, it is important to keep the same property name across both the GradeType and the concrete ConstructorArgs:

        private void SetProperty(String propertyName, object value)
        {
            var property = this.GetType().GetProperty(propertyName);
            if (property != null)
                property.SetValue(this, value);
        }
        private void ParseArguments(ConstructorArgs args)
        {
            var properties = args.GetType().GetProperties();
            foreach (PropertyInfo propertyInfo in properties)
            {
                this.SetProperty(propertyInfo.Name, 
                   args.GetType().GetProperty(propertyInfo.Name).GetValue(args));
            }
        }

4) In your GradeType class create the respective properties (mind that you must use exactly the same names and types that you used in the concrete ConstructorArgs but you can use any access modifiers you like)

    public string _gradeTypeStringFromXmlFile { get; set; }
    public Enum.GradeType _gradeType { get; set; }

5) Create a constructor for your GradeType class with a parameter of type ConstructorArgs:

    public GradeType(ConstructorArgs args)
    {
        this.ParseArguments(args);
    }

6) Now you can register the GradeType in Unity using a single constructor but you can pass in different types as arguments when resolving it:

    _unityContainer.RegisterType<IGradeType, GradeType>(
       new InjectionConstructor( typeof(ConstructorArgs) ));

    var args1 = new StringArg(gradeTypeStringFromXmlFile); // string
    IGradeType gradeType1 = _unityContainer.Resolve<IGradeType>(
       new ResolverOverride[]{new ParameterOverride("args", args1)});

    var args2 = new EnumArg(gradeType); // enum
    IGradeType gradeType2 = _unityContainer.Resolve<IGradeType>(
       new ResolverOverride[]{new ParameterOverride("args", args2)});

If you are planning to repeatedly resolve your type in an iteration that approach might not be ideal, since Reflection comes with a performance penalty.

like image 24
celetron Avatar answered Oct 25 '22 00:10

celetron