2009-09-03 25 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。

相关问题