将派生类型序列化为 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 上的基本类型的主要内容,如果未能解决你的问题,请参考以下文章

使用Boost将派生类部分反序列化为基类时输入流错误

Boost 将派生类反序列化为基类指针

在不同 AppDomain 上的序列化分类之间同步属性/字段

protobuf-net 序列化为基类

在 Asp.Net Web API 中将 JSON 反序列化为派生类型

如何在单线程客户端中将 ComVisible 类实例化为自己的 AppDomain?