动机。我有一个客户端-服务器应用程序。在某些时候,服务器端会根据某些元数据动态创建一个新类型,客户端无法使用。服务器需要向客户端发送该类型的实例。但是,客户端将无法反序列化实例,因为它的类型未知。
一种解决方案是将元数据和数据捆绑在一起,传输到客户端并让它重新创建动态类型和实例。
当特定实例深深嵌套在对象图中时,事情会变得一团糟。我想做的是将对象图按原样发送给客户端,让反序列化代码触发 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我想出了一个绝对疯狂的解决方案。它有效,但我想知道性能影响。如果我为每个动态类型只创建一个动态程序集并以该程序集的名称对类型的元数据进行编码会怎样?我检查了这种方法,它似乎有效。我得到了长达 500 个字符的程序集名称。每个程序集都定义了单个模块“DynamicModule”和单个类型 - “DynamicType”。我仍然期待更好的解决方案。