Cannot resolve a dynamically created type - Urgent

Dec 18, 2010 at 5:44 PM

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);
		}
	}
}

Dec 18, 2010 at 7:25 PM

UPDATE: There is no error if the superclass's constructor has no parameters, so I think that might narrow it down to the following code

			for (int argumentIndex = 0; argumentIndex < constructorParameters.Count(); argumentIndex++)
				bodyGenerator.Emit(OpCodes.Ldarg, argumentIndex + 1);

Dec 18, 2010 at 9:13 PM

Got it!

I wasn't defining the parameters on the new constructor.  .NET seems to use default parameters under those circumstances but doesn't default the names, so when you reflect you get the right quantity + types, but when you check the name you get a NULL and that is why Unity was giving me a null reference.

//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);