I was trying to clean up some accessability stuff in my code, and inadvertently broke Unity dependency injection. After a while I realized that I marked some public properties t
UPDATE FOR Enterprise Library 5.0
As rally52rs warned might happen, the upgrade to EntLib5.0 breaks his implementation. Using the same approach as Rally did, I reflected on the new code base and worked up the following 5.0 compatible version of the InternalConstructorSelectorPolicy.
Note that my version specifically limits itself to internal constructors in the FindLongestConstructor method. On this point my code is functionally different from Rally's.
public class InternalConstructorSelectorPolicy : IConstructorSelectorPolicy, IBuilderPolicy
{
private IDependencyResolverPolicy CreateResolver(ParameterInfo parameter)
{
List<DependencyResolutionAttribute> attrs = parameter.GetCustomAttributes(false).OfType<DependencyResolutionAttribute>().ToList<DependencyResolutionAttribute>();
if (attrs.Count > 0)
{
return attrs[0].CreateResolver(parameter.ParameterType);
}
return new NamedTypeDependencyResolverPolicy(parameter.ParameterType, null);
}
private SelectedConstructor CreateSelectedConstructor(IBuilderContext context, IPolicyList resolverPolicyDestination, ConstructorInfo ctor)
{
SelectedConstructor result = new SelectedConstructor(ctor);
foreach (ParameterInfo param in ctor.GetParameters())
{
string key = Guid.NewGuid().ToString();
IDependencyResolverPolicy policy = this.CreateResolver(param);
resolverPolicyDestination.Set<IDependencyResolverPolicy>(policy, key);
DependencyResolverTrackerPolicy.TrackKey(resolverPolicyDestination, context.BuildKey, key);
result.AddParameterKey(key);
}
return result;
}
private static ConstructorInfo FindInjectionConstructor(Type typeToConstruct)
{
ConstructorInfo[] injectionConstructors = typeToConstruct
.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where<ConstructorInfo>(delegate(ConstructorInfo ctor)
{
return ctor.IsDefined(typeof(InjectionConstructorAttribute), true);
}).ToArray<ConstructorInfo>();
switch (injectionConstructors.Length)
{
case 0:
return null;
case 1:
return injectionConstructors[0];
}
throw new InvalidOperationException(string.Format("Multiple constructors found for {0}" , typeToConstruct.Name ));
}
private static ConstructorInfo FindLongestConstructor(Type typeToConstruct)
{
var constructors =
Array.FindAll(
typeToConstruct.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic),
ctor => !ctor.IsFamily && !ctor.IsPrivate); //Filter out protected and private constructors
Array.Sort<ConstructorInfo>(constructors, new ConstructorLengthComparer());
switch (constructors.Length)
{
case 0:
return null;
case 1:
return constructors[0];
}
int paramLength = constructors[0].GetParameters().Length;
if (constructors[1].GetParameters().Length == paramLength)
{
throw new InvalidOperationException(string.Format("Ambiguous constructor found for {0}", typeToConstruct.Name));
}
return constructors[0];
}
public SelectedConstructor SelectConstructor(IBuilderContext context, IPolicyList resolverPolicyDestination)
{
Type typeToConstruct = context.BuildKey.Type;
ConstructorInfo ctor = FindInjectionConstructor(typeToConstruct) ?? FindLongestConstructor(typeToConstruct);
if (ctor != null)
{
return this.CreateSelectedConstructor(context, resolverPolicyDestination, ctor);
}
return null;
}
// Nested Types
private class ConstructorLengthComparer : IComparer<ConstructorInfo>
{
// Methods
public int Compare(ConstructorInfo x, ConstructorInfo y)
{
return (y.GetParameters().Length - x.GetParameters().Length);
}
}
}
This is my Internal Constructor Injector Extension class:
Big potential issue: 99% of this is a copy/paste of the Unity code from .NET reflector, from unity version 4.1.0.0. Newer versions of Unity may change the implementation and break this extension, or cause flakey errors. You're warned!
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;
using Microsoft.Practices.Unity.Utility;
namespace MyApp.Unity.Configuration
{
/// <summary>
/// This extension changes the behavior of Unity constructor injection to allow the use of non-public constructors.
/// By default, Unity/ObjectBuilder would call Type.GetConstructors() to get the constructors. With the default binding
/// flags, this only returns public constructors.
/// The code here is 99% copy/paste from Reflector's dissassembly of the default Unity/OB implementation.
/// My only change was to add binding flags to get all constructors, not just public ones.
/// For more info, see: Microsoft.Practices.Unity.ObjectBuilder.DefaultUnityConstructorSelectorPolicy
/// </summary>
public class InternalConstructorSelectorPolicy : IConstructorSelectorPolicy
{
protected IDependencyResolverPolicy CreateResolver(ParameterInfo param)
{
List<DependencyResolutionAttribute> list = new List<DependencyResolutionAttribute>(Sequence.OfType<DependencyResolutionAttribute>(param.GetCustomAttributes(false)));
if (list.Count > 0)
{
return list[0].CreateResolver(param.ParameterType);
}
return new NamedTypeDependencyResolverPolicy(param.ParameterType, null);
}
private SelectedConstructor CreateSelectedConstructor(IBuilderContext context, ConstructorInfo ctor)
{
SelectedConstructor constructor = new SelectedConstructor(ctor);
foreach (ParameterInfo info in ctor.GetParameters())
{
string buildKey = Guid.NewGuid().ToString();
IDependencyResolverPolicy policy = this.CreateResolver(info);
context.PersistentPolicies.Set<IDependencyResolverPolicy>(policy, buildKey);
DependencyResolverTrackerPolicy.TrackKey(context.PersistentPolicies, context.BuildKey, buildKey);
constructor.AddParameterKey(buildKey);
}
return constructor;
}
private ConstructorInfo FindInjectionConstructor(Type typeToConstruct)
{
ConstructorInfo[] infoArray = Array.FindAll<ConstructorInfo>(typeToConstruct.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), delegate(ConstructorInfo ctor)
{
return ctor.IsDefined(typeof(InjectionConstructorAttribute), true);
});
switch (infoArray.Length)
{
case 0:
return null;
case 1:
return infoArray[0];
}
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Resources.MultipleInjectionConstructors", new object[] { typeToConstruct.Name }));
}
private ConstructorInfo FindLongestConstructor(Type typeToConstruct)
{
ConstructorInfo[] constructors = typeToConstruct.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
Array.Sort<ConstructorInfo>(constructors, new ConstructorLengthComparer());
switch (constructors.Length)
{
case 0:
return null;
case 1:
return constructors[0];
}
int length = constructors[0].GetParameters().Length;
if (constructors[1].GetParameters().Length == length)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Resources.AmbiguousInjectionConstructor", new object[] { typeToConstruct.Name, length }));
}
return constructors[0];
}
public virtual SelectedConstructor SelectConstructor(IBuilderContext context)
{
Type typeToConstruct = BuildKey.GetType(context.BuildKey);
ConstructorInfo ctor = this.FindInjectionConstructor(typeToConstruct) ?? this.FindLongestConstructor(typeToConstruct);
if (ctor != null)
{
return this.CreateSelectedConstructor(context, ctor);
}
return null;
}
// Nested Types
private class ConstructorLengthComparer : IComparer<ConstructorInfo>
{
// Methods
public int Compare(ConstructorInfo x, ConstructorInfo y)
{
return (y.GetParameters().Length - x.GetParameters().Length);
}
}
}
/// <summary>
/// Registeres the InternalConstructorSelectorPolicy with the Unity container.
/// </summary>
public class InternalConstructorInjectionExtension : UnityContainerExtension
{
protected override void Initialize()
{
this.Context.Policies.SetDefault(typeof(IConstructorSelectorPolicy), new InternalConstructorSelectorPolicy());
}
}
}