Peter Morris Peter Morris - 4 months ago 29
C# Question

Unity cannot resolve instances of dynamically created class

SOLUTION
I did not specify the constructor parameters on the new type so .NET assumed the types of the base class, however it does not also name them the same so when Unity tried Parameter[x].Name it got a Null value instead of a parameter name.

Sorry to use the "U" word, but this really is a problem I need to urgently fix this problem to resolve a massive memory leak elsewhere (long story.) So if anyone can help with this I will really appreciate it!

I am dynamically creating a type at runtime using Reflection.Emit. The dynamic type descends from a base type specified at runtime, and implements IController. The purpose is to wrap the call to IController.Execute with a try...finally and dispose of an object.

Here is what is odd. I can create an instance of this new type like so

var requiredDependency = new Logger();
Type interceptingControllerType = CreateInterceptingControllerType(superClass);
var constructorInfo = interceptingControllerType.GetConstructor(new Type[] { typeof(Logger) });
var result = (IController)constructorInfo.Invoke(new object[] { requiredDependency });


I can also use unity to resolve the superclass like so

var superClassThatWillResolve = container.Resolve(superClass);


But I am unable to use unity to resolve the dynamically created subclass

var newSubClassThatWontResolve = container.Resolve(interceptingControllerType);


When I try the latter I get the following exception

Microsoft.Practices.Unity.ResolutionFailedException was unhandled
Message=Resolution of the dependency failed, type = "9d206a0c-2c78-43d1-8907-8227ad1242f1", name = "(none)".
Exception occurred while: while resolving.
Exception is: ArgumentNullException - Value cannot be null.
Parameter name: str
-----------------------------------------------
At the time of the exception, the container was:

Resolving 9d206a0c-2c78-43d1-8907-8227ad1242f1,(none)

Source=Microsoft.Practices.Unity
TypeRequested=9d206a0c-2c78-43d1-8907-8227ad1242f1
StackTrace:
at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides)
at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, String name, IEnumerable`1 resolverOverrides)
at Microsoft.Practices.Unity.UnityContainer.Resolve(Type t, String name, ResolverOverride[] resolverOverrides)
at Microsoft.Practices.Unity.UnityContainerExtensions.Resolve(IUnityContainer container, Type t, ResolverOverride[] overrides)
at ConsoleApplication18.InterceptingControllerBuilder.CreateControllerInterceptor(IUnityContainer container, Type superClass) in C:\Users\PeterMorris\Documents\Visual Studio 2010\Projects\ConsoleApplication18\InterceptingControllerBuilder.cs:line 24
at ConsoleApplication18.Program.Main(String[] args) in C:\Users\PeterMorris\Documents\Visual Studio 2010\Projects\ConsoleApplication18\Program.cs:line 15
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException: System.ArgumentNullException
Message=Value cannot be null.
Parameter name: str
Source=mscorlib
ParamName=str
StackTrace:
at System.Reflection.Emit.DynamicILGenerator.Emit(OpCode opcode, String str)
at Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy.PreBuildUp(IBuilderContext context)
at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context)
at Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlanCreatorPolicy.CreatePlan(IBuilderContext context, NamedTypeBuildKey buildKey)
at Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context)
at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context)
at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides)
InnerException:


Here is the test code...

using System;
using Microsoft.Practices.Unity;

namespace ConsoleApplication18
{
class Program
{
static void Main(string[] args)
{
var container = new UnityContainer();
var childContainer = container.CreateChildContainer();
IController interceptingController = InterceptingControllerBuilder.CreateControllerInterceptor(
childContainer, typeof(CountryController));
try
{
interceptingController.Execute("Hello");
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e.Message);
}
Console.ReadLine();
}
}

public class Logger
{
public void Log(string text)
{
Console.WriteLine(text);
}
}

public interface IController
{
void Execute(string text);
}

public class Controller : IController
{
readonly Logger Logger;

public Controller(Logger logger)
{
Logger = logger;
}

public virtual void Execute(string text)
{
Logger.Log("Controller: " + text);
}
}

public class CountryController : Controller
{
public CountryController(Logger logger)
: base(logger)
{
}

public override void Execute(string text)
{
base.Execute("CountryController: " + text);
}
}
}


And the implementation code

using Microsoft.Practices.Unity;

namespace ConsoleApplication18
{
public interface IUnityContainerController
{
IUnityContainer IUnityContainerController_UnityContainer { get; set; }
}
}




using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using Microsoft.Practices.Unity;

namespace ConsoleApplication18
{
public static class InterceptingControllerBuilder
{
const string UnityContainerBackingFieldName = "IUnityContainerController_BackingField";
static MethodInfo DisposeMethodInfo = typeof(IDisposable).GetMethod("Dispose");

public static IController CreateControllerInterceptor(IUnityContainer container, Type superClass)
{
var requiredDependency = new Logger();
Type interceptingControllerType = CreateInterceptingControllerType(superClass);
var constructorInfo = interceptingControllerType.GetConstructor(new Type[] { typeof(Logger) });
var result = (IController)constructorInfo.Invoke(new object[] { requiredDependency });
var resultAsIUnityContainerController = (IUnityContainerController)result;
resultAsIUnityContainerController.IUnityContainerController_UnityContainer = container;

var superClassThatWillResolve = container.Resolve(superClass);
var newSubClassThatWontResolve = container.Resolve(interceptingControllerType);

return result;
}

static Type CreateInterceptingControllerType(Type superClass)
{
if (!typeof(IController).IsAssignableFrom(superClass))
throw new ArgumentException("SuperClass does not implement IController");

string guid = Guid.NewGuid().ToString();
var assemblyName = new AssemblyName(guid);
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
assemblyName,
AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule(guid);
var typeBuilder = moduleBuilder.DefineType(
guid,
TypeAttributes.Class | TypeAttributes.Public,
superClass);
CreateConstructor(superClass, typeBuilder);
FieldBuilder unityContainerBackingFieldBuilder;
ImplementIUnityContainerController(typeBuilder, out unityContainerBackingFieldBuilder);
ImplementIController(superClass, typeBuilder, unityContainerBackingFieldBuilder);
return typeBuilder.CreateType();
}

static void CreateConstructor(Type superClass, TypeBuilder typeBuilder)
{
var constructorInfo = superClass.GetConstructors()
.OrderByDescending(x => x.GetParameters().Count())
.FirstOrDefault();
if (constructorInfo == null)
return;
ParameterInfo[] constructorParameters =
constructorInfo.GetParameters().ToArray();
Type[] parameterTypes = constructorParameters.Select(x => x.ParameterType).ToArray();
var constructorBuilder = typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
parameterTypes);
var bodyGenerator = constructorBuilder.GetILGenerator();
bodyGenerator.Emit(OpCodes.Ldarg_0);
for (int argumentIndex = 0; argumentIndex < constructorParameters.Count(); argumentIndex++)
bodyGenerator.Emit(OpCodes.Ldarg, argumentIndex + 1);
bodyGenerator.Emit(OpCodes.Call, constructorInfo);
bodyGenerator.Emit(OpCodes.Ret);
}

static void ImplementIUnityContainerController(TypeBuilder typeBuilder, out FieldBuilder unityContainerBackingFieldBuilder)
{
typeBuilder.AddInterfaceImplementation(typeof(IUnityContainerController));
unityContainerBackingFieldBuilder = typeBuilder.DefineField(
UnityContainerBackingFieldName,
typeof(IUnityContainer),
FieldAttributes.Private);

var propertyAccessorAttributes =
MethodAttributes.Public | MethodAttributes.SpecialName |
MethodAttributes.HideBySig | MethodAttributes.Virtual;

var getterBuilder = typeBuilder.DefineMethod(
"get_IUnityContainerController_UnityContainer",
propertyAccessorAttributes,
typeof(IUnityContainer),
Type.EmptyTypes);
var getterGenerator = getterBuilder.GetILGenerator();
getterGenerator.Emit(OpCodes.Ldarg_0);
getterGenerator.Emit(OpCodes.Ldfld, unityContainerBackingFieldBuilder);
getterGenerator.Emit(OpCodes.Ret);

var setterBuilder = typeBuilder.DefineMethod(
"set_IUnityContainerController_UnityContainer",
propertyAccessorAttributes,
null,
new Type[] { typeof(IUnityContainer) });
var setterGenerator = setterBuilder.GetILGenerator();
setterGenerator.Emit(OpCodes.Ldarg_0);
setterGenerator.Emit(OpCodes.Ldarg_1);
setterGenerator.Emit(OpCodes.Stfld, unityContainerBackingFieldBuilder);
setterGenerator.Emit(OpCodes.Ret);
}

static void ImplementIController(Type superClass, TypeBuilder typeBuilder, FieldBuilder unityContainerBackingFieldBuilder)
{
typeBuilder.AddInterfaceImplementation(typeof(IController));
MethodInfo interfaceMethod = typeof(IController).GetMethod("Execute");
InterfaceMapping mapping = superClass.GetInterfaceMap(typeof(IController));
MethodInfo baseMethod = mapping.TargetMethods.Single();

var methodBuilder = typeBuilder.DefineMethod(
typeof(IController).Name + ".Execute",
MethodAttributes.Public | MethodAttributes.Virtual |
MethodAttributes.ReuseSlot | MethodAttributes.HideBySig,
null,
new Type[] { typeof(string) });
var bodyGenerator = methodBuilder.GetILGenerator();
bodyGenerator.BeginExceptionBlock();
bodyGenerator.Emit(OpCodes.Ldarg_0);
bodyGenerator.Emit(OpCodes.Ldarg_1);
bodyGenerator.Emit(OpCodes.Call, baseMethod);
bodyGenerator.BeginFinallyBlock();
bodyGenerator.Emit(OpCodes.Ldarg_0);
bodyGenerator.Emit(OpCodes.Ldfld, unityContainerBackingFieldBuilder);
bodyGenerator.Emit(OpCodes.Call, DisposeMethodInfo);
bodyGenerator.EndExceptionBlock();
bodyGenerator.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(methodBuilder, interfaceMethod);
}
}
}

Answer

The parameters for the constructor on the new type were not defined so .NET created some default parameters which had NULL for names!

//Define the parameters for our new constructor
for (int argumentIndex = 0; argumentIndex < parameterTypes.Length; argumentIndex++)
    constructorBuilder.DefineParameter(
        argumentIndex + 1,
        constructorParameters[argumentIndex].Attributes,
        constructorParameters[argumentIndex].Name);