動機。クライアント/サーバー アプリケーションがあります。ある時点で、サーバー側は特定のメタデータに基づいて新しいタイプを動的に作成しますが、クライアントには使用できません。サーバーは、型のインスタンスをクライアントに送信する必要があります。ただし、インスタンスのタイプが不明であるため、クライアントはインスタンスのデシリアライズに失敗します。
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
返されるはです。OnAssemblyResolve
null
誰もそれを行う方法を知っていますか?
編集: 2 回の往復で行うことができます。最初にメタデータを渡し、2 番目にオブジェクト自体を渡します。1往復の解決策を見つけたいと思います。
EDIT:2私は絶対にクレイジーな解決策を思いつきました。動作しますが、パフォーマンスへの影響について疑問に思っています。動的型ごとに 1 つの動的アセンブリを作成し、そのアセンブリの名前で型のメタデータをエンコードするとどうなりますか? 私はこのアプローチをチェックしましたが、うまくいっているようです。最大 500 文字の長さのアセンブリ名を取得しました。各アセンブリは、単一のモジュール「DynamicModule」と単一のタイプ「DynamicType」を定義します。それでも私はより良い解決策を楽しみにしています。