是否有可能在AssemblyResolve方法中有额外的上下文?

时间:2009-09-03 12:13:29

标签: .net assembly-resolution

动机。我有一个客户端 - 服务器应用程序。在某些时候,服务器端会根据客户端无法使用的某些元数据动态创建新类型。服务器需要将类型的实例发送到客户端。但是,客户端将无法反序列化实例,因为其类型未知。

一种解决方案是将元数据和数据捆绑在一起,传输到客户端并让它重新创建动态类型和实例。

当特定实例深深嵌套在对象图中时,事情变得混乱。我想要做的是将对象图原样发送到客户端,让反序列化代码触发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);
    }
  }
}

data事件处理程序中返回的OnAssemblyResolvenull

有谁知道怎么做?

编辑:可以在两次往返中完成 - 在第一次传递元数据,在第二次传递对象本身。我想找一个往返解决方案。

编辑:2 我想出了一个绝对疯狂的解决方案。它有效,但我想知道性能影响。如果我为每个动态类型创建一个动态集合并在该程序集的名称中编码类型的元数据,该怎么办?我检查了这种方法,似乎工作正常。我得到了500个字符长的程序集名称。每个程序集定义单个模块“DynamicModule”和单个类型 - “DynamicType”。我仍然期待着更好的解决方案。

1 个答案:

答案 0 :(得分:0)

您可以将非静态方法注册为AppDomain.AssemblyResolve事件处理程序。然后,您可以访问实例的成员,该方法已注册。它与我在这里提出的AssemblyResolver类非常相似:

Need to hookup AssemblyResolve event when DisallowApplicationBaseProbing = true

在反序列化时,您可以在AssemblyResolve事件被触发之前将元数据存储在AssemblyResolver实例中。有趣的是“何时”将元数据存储到AssemblyResolver。坚持单个反序列化运行需要您在包含动态类型对象的对象中实现元数据的反序列化。也许您可以将动态对象放在一个包装器中以方便使用。让包装器带来元数据和动态类型对象(后者序列化为字符串或字节[],具体取决于您的序列化)。自定义包装器的反序列化过程,首先将元数据推送到AssemblyResolver。然后从包装器的string或byte []成员反序列化动态类型对象。

访问AssemblyResolver的最简单的解决方案可能是单例模式,尽管许多人投票支持依赖注入。

实际上,对于部分对象结构,您将“本地”进行递归反序列化运行。但是,我没有看到任何对高级对象结构反序列化的影响。请注意,此解决方案需要一些额外的工作才能获得线程安全,因为在推送元数据之前需要阻止AssemblyResolver。如果存在另一个动态类型对象的动态类型对象,则会出现问题,因为那时您需要在AssemblyResolve事件处理结束时释放AssemblyResolver。