2010-12-18 56 views
2

SOLUTION 我并没有对新类型指定构造参数,使.NET假设类型的基类的,但它不也是他们的名字一样,所以当统一试过参数[x] .Name它得到一个空值而不是参数名称。团结不能解决动态创建类的实例

对不起,使用“U”这个词,但这真的是一个问题,我需要紧急解决这个问题,以解决其他地方的大量内存泄漏(长篇故事)。所以,如果任何人都可以提供帮助,我将非常感激它!

我在运行时使用Reflection.Emit动态创建一个类型。动态类型从运行时指定的基类型下降,并实现IController。目的是将调用包装到IController。执行一个try ... finally并最终处理一个对象。

这是奇怪的。我可以像这样

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 superClassThatWillResolve = container.Resolve(superClass); 

创建这个新类型的实例但我无法用团结来解决动态创建的子类

var newSubClassThatWontResolve = container.Resolve(interceptingControllerType); 

当我尝试后我得到下面的异常

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: 

下面是测试代码...

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

而且实现代码

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

更新:如果超类没有参数,我不会收到错误。 – 2010-12-18 20:23:47

回答

2

新类型的构造函数的参数没有定义,所以.NET创建了一些默认参数,其名称为NULL!

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

我会说这是因为是动态创建的类型。而不是这样做,为什么不注册一个工厂函数(使用InjectionFactory)对该类型?这将阻止Unity尝试创建类型本身,而是使用自定义函数来创建实例。

顺便说一句,你为什么要创建像这样的拦截器?你可以在Unity中创建一个拦截器,并通过它来连接,而不是动态创建拦截器类型。这将首先消除所有这些代码的需要。

+0

从我可以看到Unity需要我为每个Controller类注册一个拦截器。我想要的是封装IController的ControllerFactory。不管类的类型是什么,都执行。 – 2010-12-18 22:10:34