将派生类型序列化为 AppDomain 上的基本类型
Posted
技术标签:
【中文标题】将派生类型序列化为 AppDomain 上的基本类型【英文标题】:Serialize derived type as base type over AppDomain 【发布时间】:2017-07-06 03:12:25 【问题描述】:我有一个插件系统,插件本身可以有多个运行时实例。这些实例是使用配置对象定义的,该对象通过各种 API 提供给插件本身。
配置包含父应用程序可以理解的特定部分,但插件需要通过子类化指定自己的部分。
[Serializable]
public class PluginConfiguration
public string Name get; set;
public class MyPluginConfiguration : PluginConfiguration
public string AccessToken get; set;
这部分是技术原因,因为它允许主应用程序将整个对象序列化为 JSON/XML/等。存入数据库以持久化通用配置和插件特定配置。
每个插件都托管在自己的 AppDomain 中。但是,当我尝试从主应用程序域和插件读取插件配置时遇到了麻烦(MarshalByRefObject
返回其 MyPluginConfiguration
对象实例。
有没有办法在基类中做一些序列化魔术来确保对象实例被序列化为PluginConfiguration
实例?序列化MyPluginConfiguration
实例的问题是:
[Serializable]
即使他们是[Serializable]
,他们也不会在主应用程序域中加载他们的程序集。
理想情况下,我希望实现仅限于 PluginConfiguration
基类。旧的 [Serializable]
语义和 AppDomains 不是 .Net 中最常见的概念,如果插件开发人员不需要处理这些方面,我想隐藏其中的一些方面。
【问题讨论】:
【参考方案1】:使用二进制序列化,根据您的[Serializable]
注意:XML 有 IXmlSerializable 和 javascript 也不同。我相信 JSON.net 序列化 ISerializable 对象如下实现
[Serializable]
public class PluginConfiguration : System.Runtime.Serialization.ISerializable
public string Name get; set;
public PluginConfiguration()
protected PluginConfiguration(SerializationInfo info, StreamingContext context)
if (info == null)
throw new System.ArgumentNullException("info");
Name = (string)info.GetValue("Name", typeof(string));
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
info.AddValue("Name", Name);
[Serializable]
public class MyPluginConfiguration : PluginConfiguration
public string AccessToken get; set;
public MyPluginConfiguration()
protected MyPluginConfiguration(SerializationInfo info, StreamingContext context) :base(info, context)
private class PluginBinder : SerializationBinder
public override Type BindToType(string assemblyName, string typeName)
return typeof(PluginConfiguration);
用法:
var c = new PluginConfiguration() Name = "Name" ;
var p = new MyPluginConfiguration() Name = "Name", AccessToken = "MyToken" ;
BinaryFormatter fmt = new BinaryFormatter();
MemoryStream cms = new MemoryStream();
fmt.Serialize(cms, c);
cms.Position = 0;
PluginConfiguration revc = fmt.Deserialize(cms) as PluginConfiguration;
MemoryStream pms = new MemoryStream();
fmt.Serialize(pms, p);
pms.Position = 0;
MyPluginConfiguration prev = fmt.Deserialize(pms) as MyPluginConfiguration;
//given a stream containing the serialization of MyPluginConfiguration
// serailize it as a the known PluginConfiguration Type
pms.Position = 0;
fmt.Binder = new PluginBinder();
PluginConfiguration PluginFromCustomStream = fmt.Deserialize(pms) as PluginConfiguration;
【讨论】:
我需要的是fmt.Serialize(pms,p);fmt.Deserialize(pms) as PluginConfiguration
。插件应用程序域将使用MyPluginConfiguration
类型执行Serialize
,而应用程序应用程序域将没有MyPluginConfiguration
可用,因此不能将其用于Deserialize
,所以我需要能够让Deserialize
改用PluginConfiguration
。
我在答案中添加了一个自定义活页夹,以向您展示如何完成此操作。
这听起来很有用。之前没遇到过SerializationBinder
类型。您如何定义一个供应用程序域使用?
如果您只想序列化基类,您可以使用我的示例中的基类。
如果IPlugin foo
是一个透明代理实例,它指向另一个应用程序域和宿主应用程序域中的插件实现,我调用foo.Configuration
,这是一个返回PluginConfiguration
的属性,远程处理基础设施为我进行序列化。不涉及手动BinaryFormatter
。我如何告诉AppDomain
实例或透明代理实例在跨应用程序域边界传输[Serializable]
对象时进行序列化时应该使用我的PluginBinder
?以上是关于将派生类型序列化为 AppDomain 上的基本类型的主要内容,如果未能解决你的问题,请参考以下文章
在不同 AppDomain 上的序列化分类之间同步属性/字段