简体   繁体   中英

Serialize derived type as base type over AppDomain

I've got a plugin system where the plugin itself can have several runtime instances. These instances are defined with a configuration object that gets carried is made available to the plugin itself through various APIs.

The configuration contains specific parts that the parent application understands but it is expected for the plugin to specify its own parts through subclassing.

[Serializable]
public class PluginConfiguration {
    public string Name { get; set; }
}

public class MyPluginConfiguration : PluginConfiguration {
    public string AccessToken { get; set; }
}

This is partly technical reason as it allows the main application to just serialize the whole object as JSON/XML/etc. into database to persist both the common configuration and the plugin specific configuration.

Each plugin is hosted within its own AppDomain. However I'm getting into trouble when I'm trying to read the plugin configuration from the primary app domain and the plugin ( MarshalByRefObject returns its MyPluginConfiguration object instance.

Is there a way to do some Serialization magic in the base class to ensure that the object instances are serialized as PluginConfiguration instances? The problem in serializing the MyPluginConfiguration instances are:

  • The custom configuration type might not be [Serializable]
  • Even if they were [Serializable] , they don't have their Assembly loaded in the primary app domain anyway.

Ideally I'd like the implementation to be restricted to the PluginConfiguration base class. The old [Serializable] semantics and AppDomains are not the most common concept in .Net and I would like to hide some of these aspects from the plugin developers if they don't need to deal with them.

Using binary serialization, per your [Serializable]

Note: XML has IXmlSerializable and Javascript is also different. I believe JSON.net serializes ISerializable objects as implemented below

[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);
    }
}

Usage:

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM