【问题标题】:Serialize derived type as base type over AppDomain将派生类型序列化为 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 中最常见的概念,如果插件开发人员不需要处理这些方面,我想隐藏其中的一些方面。

【问题讨论】:

    标签: c# .net serialization appdomain remoting


    【解决方案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
    猜你喜欢
    • 1970-01-01
    • 2011-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    相关资源
    最近更新 更多