I\'m using Microsoft Unity. I have an interface ICustomerService
and its implementation CustomerService
. I can register them for the Unity containe
Chris Tavares gave a good answer with a lot of information.
If you have many Parameters to inject usually these are interfaces or instances which can be resolved by Unity (using the differnet techniques). But what if you want to provide only one Parameter which cannot be resolved automatically, e.g. a string for a filename?
Now you have to provide all the typeof(IMyProvider)
and one string or instance. But to be honest just providing the types could be done by Unity, because Unity has already a strategy to choose the best ctor.
So I coded a replacement for InjectionConstructor
:
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;
using Microsoft.Practices.Unity.Utility;
namespace Microsoft.Practices.Unity
{
///
/// A class that holds the collection of information for a constructor,
/// so that the container can be configured to call this constructor.
/// This Class is similar to InjectionConstructor, but you need not provide
/// all Parameters, just the ones you want to override or which cannot be resolved automatically
/// The given params are used in given order if type matches
///
public class InjectionConstructorRelaxed : InjectionMember
{
private List _parameterValues;
///
/// Create a new instance of that looks
/// for a constructor with the given set of parameters.
///
/// The values for the parameters, that will
/// be converted to objects.
public InjectionConstructorRelaxed(params object[] parameterValues)
{
_parameterValues = InjectionParameterValue.ToParameters(parameterValues).ToList();
}
///
/// Add policies to the to configure the
/// container to call this constructor with the appropriate parameter values.
///
/// Interface registered, ignored in this implementation.
/// Type to register.
/// Name used to resolve the type object.
/// Policy list to add policies to.
public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies)
{
ConstructorInfo ctor = FindExactMatchingConstructor(implementationType);
if (ctor == null)
{
//if exact matching ctor not found, use the longest one and try to adjust the parameters.
//use given Params if type matches otherwise use the type to advise Unity to resolve later
ctor = FindLongestConstructor(implementationType);
if (ctor != null)
{
//adjust parameters
var newParams = new List();
foreach (var parameter in ctor.GetParameters())
{
var injectionParameterValue =
_parameterValues.FirstOrDefault(value => value.MatchesType(parameter.ParameterType));
if (injectionParameterValue != null)
{
newParams.Add(injectionParameterValue);
_parameterValues.Remove(injectionParameterValue);
}
else
newParams.Add(InjectionParameterValue.ToParameter(parameter.ParameterType));
}
_parameterValues = newParams;
}
else
{
throw new InvalidOperationException(
string.Format(
CultureInfo.CurrentCulture,
"No constructor found for type {0}.",
implementationType.GetTypeInfo().Name));
}
}
policies.Set(
new SpecifiedConstructorSelectorPolicy(ctor, _parameterValues.ToArray()),
new NamedTypeBuildKey(implementationType, name));
}
private ConstructorInfo FindExactMatchingConstructor(Type typeToCreate)
{
var matcher = new ParameterMatcher(_parameterValues);
var typeToCreateReflector = new ReflectionHelper(typeToCreate);
foreach (ConstructorInfo ctor in typeToCreateReflector.InstanceConstructors)
{
if (matcher.Matches(ctor.GetParameters()))
{
return ctor;
}
}
return null;
}
private static ConstructorInfo FindLongestConstructor(Type typeToConstruct)
{
ReflectionHelper typeToConstructReflector = new ReflectionHelper(typeToConstruct);
ConstructorInfo[] constructors = typeToConstructReflector.InstanceConstructors.ToArray();
Array.Sort(constructors, new ConstructorLengthComparer());
switch (constructors.Length)
{
case 0:
return null;
case 1:
return constructors[0];
default:
int paramLength = constructors[0].GetParameters().Length;
if (constructors[1].GetParameters().Length == paramLength)
{
throw new InvalidOperationException(
string.Format(
CultureInfo.CurrentCulture,
"The type {0} has multiple constructors of length {1}. Unable to disambiguate.",
typeToConstruct.GetTypeInfo().Name,
paramLength));
}
return constructors[0];
}
}
private class ConstructorLengthComparer : IComparer
{
///
/// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
///
/// The second object to compare.
/// The first object to compare.
///
/// Value Condition Less than zero is less than y. Zero equals y. Greater than zero is greater than y.
///
[SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class")]
public int Compare(ConstructorInfo x, ConstructorInfo y)
{
Guard.ArgumentNotNull(x, "x");
Guard.ArgumentNotNull(y, "y");
return y.GetParameters().Length - x.GetParameters().Length;
}
}
}
}
Usage:
container.RegisterType(new TransientLifetimeManager(), new InjectionConstructorRelaxed(
new SomeService1("with special options")
//, new SomeService2() //not needed, normal unity resolving used
//equivalent to: , typeof(SomeService2)
));