2010-12-18 57 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