2009-09-03 51 views
1

動機。我有一個客戶端 - 服務器應用程序。在某些時候,服務器端會根據某些元數據動態地創建一個新類型,這對客戶端是不可用的。服務器需要發送一個類型的實例給客戶端。但是,客戶端將無法反序列化實例,因爲它的類型是未知的。是否有可能在AssemblyResolve方法中有額外的上下文?

一個解決方案是將元數據和數據捆綁在一起,傳輸給客戶端,讓它重新創建動態類型和實例。

當特定實例深深地嵌套在對象圖中時,事情會變得雜亂無章。我想要做的是將對象圖發送到客戶端,讓反序列化代碼激發AppDomain.AssemblyResolved事件並在那裏重新創建相應的動態類型。唉!我做不到,因爲我不知道如何使元數據可用於事件處理程序。

我試過使用CallContext,但沒有奏效。

這裏是我用來尋找解決方案,這是我沒有成功,在整個樣本代碼:

using System; 
using System.Reflection; 
using System.Reflection.Emit; 
using System.Runtime.Remoting.Messaging; 
using System.Security; 
using System.Security.Permissions; 

namespace DynamicTypes 
{ 
    [Serializable] 
    public class LogicalCallContextData : ILogicalThreadAffinative 
    { 
    public string DynamicAssemblyName { get; private set; } 
    public string DynamicTypeName { get; private set; } 

    public LogicalCallContextData(string dynamicAssemblyName, string dynamicTypeName) 
    { 
     DynamicAssemblyName = dynamicAssemblyName; 
     DynamicTypeName = dynamicTypeName; 
    } 
    } 

    class Program 
    { 
    private static string DynamicAssemblyName; 
    private static string DynamicTypeName; 
    private static Type m_type; 

    static void CreateDynamicType() 
    { 
     if (m_type == null) 
     { 
     var assemblyName = new AssemblyName(DynamicAssemblyName); 
     var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); 
     var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); 
     var typeBuilder = moduleBuilder.DefineType(DynamicTypeName, TypeAttributes.Public | TypeAttributes.Serializable, typeof(object)); 
     var constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes); 
     var ilGenerator = constructorBuilder.GetILGenerator(); 
     ilGenerator.Emit(OpCodes.Ldarg_0); 
     ilGenerator.Emit(OpCodes.Call, typeof(object).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null)); 
     ilGenerator.Emit(OpCodes.Ret); 
     m_type = typeBuilder.CreateType(); 
     } 
    } 

    static void AppDomainInitialize(string[] args) 
    { 
     AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve; 
    } 

    static Assembly OnAssemblyResolve(object sender, ResolveEventArgs args) 
    { 
     var data = (LogicalCallContextData)CallContext.GetData("test data"); 
     if (data != null) 
     { 
     DynamicAssemblyName = data.DynamicAssemblyName; 
     DynamicTypeName = data.DynamicTypeName; 

     CreateDynamicType(); 
     if (m_type.Assembly.FullName == args.Name) 
     { 
      return m_type.Assembly; 
     } 
     } 
     return null; 
    } 

    [Serializable] 
    private class CrossAppDomain 
    { 
     private object m_obj; 
     public CrossAppDomain() 
     { 
     CreateDynamicType(); 
     m_obj = Activator.CreateInstance(m_type); 
     } 

     public void DoIt() 
     { 
     } 
    } 

    [PermissionSet(SecurityAction.LinkDemand)] 
    static void Main(string[] args) 
    { 
     DynamicAssemblyName = Guid.NewGuid().ToString("N"); 
     DynamicTypeName = Guid.NewGuid().ToString("N"); 

     var data = new LogicalCallContextData(DynamicAssemblyName, DynamicTypeName); 
     CallContext.SetData("test data", data); 

     AppDomainInitialize(null); 
     var appDomainSetup = new AppDomainSetup(); 
     appDomainSetup.AppDomainInitializer = AppDomainInitialize; 
     var appDomain = AppDomain.CreateDomain("second", null, appDomainSetup); 
     appDomain.DoCallBack(new CrossAppDomain().DoIt); 
    } 
    } 
} 

dataOnAssemblyResolve事件處理函數返回是null

有誰知道該怎麼做?

編輯:可以在兩次往返中完成 - 首先傳遞元數據,然後在第二次傳遞對象本身。我想找到一個往返解決方案。

編輯:2我想出了一個絕對瘋狂的解決方案。它的工作原理,但我想知道性能影響。如果我爲每個動態類型創建一個動態程序集,並在該程序集的名稱中對類型的元數據進行編碼,該怎麼辦?我檢查了這種方法,它似乎工作。我有500個字符長的組合名稱。每個程序集定義單個模塊「DynamicModule」和單個類型 - 「DynamicType」。我仍然期待更好的解決方案。

回答

0

您可以註冊一個非靜態方法作爲AppDomain.AssemblyResolve事件處理程序。然後您可以訪問實例的成員,該方法已註冊。這是相當喜歡AssemblyResolver類我在這裏:

Need to hookup AssemblyResolve event when DisallowApplicationBaseProbing = true

在反序列化,您可以在AssemblyResolver實例的元數據存儲之前AssemblyResolve事件時觸發。有趣的是「何時」將元數據存儲到AssemblyResolver中。堅持一個反序列化運行需要您在保存動態類型對象的對象中實現元數據的反序列化。也許你可以將動態對象放在一個包裝器中以方便使用。使包裝帶來元數據和動態類型的對象(後者根據序列化序列化爲字符串或字節[])。定製包裝器的反序列化過程以首先將元數據推送到AssemblyResolver。然後從包裝器的字符串或byte []成員中反序列化動態類型的對象。

訪問AssemblyResolver的最簡單的解決方案可能是singleton模式,儘管許多投票依賴注入來代替。

事實上,你將有遞歸反序列化運行「本地」的部分對象結構。但是,我沒有看到任何對高級對象結構反序列化的影響。請注意,此解決方案需要一些額外的工作才能獲得線程安全,因爲在推送元數據之前需要先阻止AssemblyResolver。如果存在包含另一個動態類型對象的動態類型對象,則會出現問題,因爲那樣您需要在AssemblyResolve事件處理結束時釋放AssemblyResolver。

相關問題