1

動機。クライアント/サーバー アプリケーションがあります。ある時点で、サーバー側は特定のメタデータに基づいて新しいタイプを動的に作成しますが、クライアントには使用できません。サーバーは、型のインスタンスをクライアントに送信する必要があります。ただし、インスタンスのタイプが不明であるため、クライアントはインスタンスのデシリアライズに失敗します。

1 つの解決策は、メタデータとデータの両方をまとめてクライアントに送信し、動的な型とインスタンスを再作成させることです。

特定のインスタンスがオブジェクト グラフ内で深くネストされている場合、事態は複雑になります。私がやりたいことは、オブジェクト グラフをそのままクライアントに送信し、逆シリアル化コードで 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 回の往復で行うことができます。最初にメタデータを渡し、2 番目にオブジェクト自体を渡します。1往復の解決策を見つけたいと思います。

EDIT:2私は絶対にクレイジーな解決策を思いつきました。動作しますが、パフォーマンスへの影響について疑問に思っています。動的型ごとに 1 つの動的アセンブリを作成し、そのアセンブリの名前で型のメタデータをエンコードするとどうなりますか? 私はこのアプローチをチェックしましたが、うまくいっているようです。最大 500 文字の長さのアセンブリ名を取得しました。各アセンブリは、単一のモジュール「DynamicModule」と単一のタイプ「DynamicType」を定義します。それでも私はより良い解決策を楽しみにしています。

4

1 に答える 1

0

非静的メソッドを AppDomain.AssemblyResolve イベント ハンドラーとして登録できます。次に、メソッドが登録されたインスタンスのメンバーにアクセスできます。ここで紹介する AssemblyResolver クラスによく似ています。

DisallowApplicationBaseProbing = true の場合、AssemblyResolve イベントを接続する必要がある

逆シリアル化では、AssemblyResolve イベントが発生する前に、AssemblyResolver インスタンスにメタデータを格納できます。興味深い点は、"いつ" メタデータを AssemblyResolver に保存するかということです。単一の逆シリアル化の実行を主張するには、動的型のオブジェクトを保持するオブジェクトにメタデータの逆シリアル化を実装する必要があります。便宜上、動的オブジェクトを一種のラッパーに入れることができるかもしれません。ラッパーにメタデータと動的に型指定されたオブジェクト (後者はシリアル化に応じて文字列またはバイト [] にシリアル化されます) をもたらします。ラッパーの逆シリアル化プロセスをカスタマイズして、最初にメタデータを AssemblyResolver にプッシュします。次に、ラッパーの string または byte[] メンバーから動的型指定オブジェクトを逆シリアル化します。

おそらくAssemblyResolverにアクセスするための最も簡単な解決策はシングルトンパターンですが、代わりに多くの人が依存性注入に投票しています.

実際、オブジェクト構造の一部に対して再帰的なデシリアライゼーションを「ローカルに」実行することになります。ただし、高レベルのオブジェクト構造の逆シリアル化には何の影響もありません。メタデータをプッシュする前に AssemblyResolver をブロックする必要があるため、このソリューションをスレッドセーフにするために追加の作業が必要になることに注意してください。AssemblyResolve イベント処理の最後に AssemblyResolver を解放する必要があるため、別の動的型指定オブジェクトを保持する動的型指定オブジェクトがある場合、問題が発生します。

于 2014-01-23T15:00:39.397 に答える