在不同 AppDomain 上的序列化分类之间同步属性/字段
Posted
技术标签:
【中文标题】在不同 AppDomain 上的序列化分类之间同步属性/字段【英文标题】:Synchronizing properties / fields between serialized classed on different AppDomains 【发布时间】:2013-06-24 04:20:21 【问题描述】:情况就是这样——我有一个派生自抽象基类的类(因此不能从 MarshalByRefObject 派生并用作代理对象),并且它具有正确的状态很重要,因为它正在处理与流并做一些重要的事情。
它被标记为 Serializable 因为我需要将它传递给另一个 AppDomain 并让该 appdomain 定期调用 .Post(string)
方法。
我对 appdomains 一无所知,但我看到了一些奇怪的事情,我假设该类实际上在第二个 AppDomain 中使用相同的字段和属性重新实例化,然后我最终得到了多个实例类的。
这是一个问题,因为当第二个 AppDomain 调用 .Post()
时,它会修改流和它自己的对象状态,但主 appdomain 中的类中包含的私有变量没有更新 - 所以有主应用程序域中的一个损坏状态,一旦主应用程序域在其自己的类版本上调用.Post
,我就会得到一个损坏的流!
我也不能使用包装类,因为我需要传递作为其抽象基类的对象,而第二个appdomain不知道派生类的类型是什么,只知道调用@ 987654324@是基类中定义的抽象方法。
有什么方法可以将第二个 appdomain 对类所做的更改更新回命令上的主 appdomain 字段,以便我可以“同步”字段的状态?
【问题讨论】:
【参考方案1】:我认为您可以使用 MarshalByRefObject
包装类来完成这项工作。
考虑以下示例:
using System;
using System.IO;
using System.Reflection;
namespace ConsoleApplication13
class Program
static void Main(string[] args)
AppDomain appDomain = AppDomain.CreateDomain("foo");
FooFactory fooFactory = (FooFactory)appDomain.CreateInstanceFromAndUnwrap(Assembly.GetEntryAssembly().Location, typeof(FooFactory).FullName);
IFoo fooFromOtherDomain = fooFactory.CreateMeAFoo();
string message = "Hello World!";
Console.WriteLine("Data = 0 on AppDomain ID = 1", message, AppDomain.CurrentDomain.Id);
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(message);
fooFromOtherDomain.Post(buffer);
public interface IFoo
void Post(byte[] data);
public abstract class FooBase
/// <summary>
/// This class represents your class that can't be marshaled by ref...
/// </summary>
public class Foo : FooBase, IFoo, IDisposable
private MemoryStream _buffer;
public Foo()
this._buffer = new MemoryStream();
public void Post(byte[] data)
if (data == null)
throw new ArgumentNullException("data");
this._buffer.Seek(0, SeekOrigin.End);
this._buffer.Write(data, 0, data.Length);
OnNewData();
private void OnNewData()
string dataString = System.Text.Encoding.UTF8.GetString(this._buffer.ToArray());
Console.WriteLine("Data = 0 on AppDomain ID = 1", dataString, AppDomain.CurrentDomain.Id);
public void Dispose()
this._buffer.Close();
/// <summary>
/// Wraps the non-remote Foo class and makes it remotely accessible.
/// </summary>
public class FooWrapper : MarshalByRefObject, IFoo
private IFoo _innerFoo;
public FooWrapper(IFoo innerFoo)
this._innerFoo = innerFoo;
public void Post(byte[] data)
this._innerFoo.Post(data);
/// <summary>
/// For demo purposes to get an instance of IFoo from the other domain.
/// </summary>
public class FooFactory : MarshalByRefObject
public IFoo CreateMeAFoo()
Foo foo = new Foo();
FooWrapper fooWrapper =new FooWrapper(foo);
return fooWrapper;
不要太在意 FooFactory 类。这只是为了方便远程创建展示您的场景的对象。
基本上为您的远程类定义一个接口,例如IFoo
。
public interface IFoo
void Post(byte[] data);
创建一个派生自MarshalByRefObject
并实现IFoo
的Foo
包装类
/// <summary>
/// Wraps the non-remote Foo class and makes it remotely accessible.
/// </summary>
public class FooWrapper : MarshalByRefObject, IFoo
private IFoo _innerFoo;
public FooWrapper(IFoo innerFoo)
this._innerFoo = innerFoo;
public void Post(byte[] data)
this._innerFoo.Post(data);
将您的 IFoo 实现传递给 FooWrapper
。
Foo foo = new Foo();
FooWrapper fooWrapper = new FooWrapper(foo);
然后将FooWrapper
返回到另一个域并像往常一样调用IFoo
方法,如Post
。
这个程序的执行输出:
【讨论】:
这可以工作 - 但需要做很多工作,因为现在一切都被转换回抽象类的类型。为了做到这一点,我需要定义一个新接口并使用它而不是基类 @caesay- 只有您需要跨域访问的公共方法和属性才需要在接口中。 [编辑-我明白你的意思,你已经在你的调用中引用了基类,重构是很多工作,你在使用 Visual Studio 吗?它可以帮助重构您的代码。无论如何,跨 AppDomains 传递基类可能不是最好的事情。不如使用接口灵活] 或者,您是否控制抽象基类?你能让它派生自 MarshalByRefObject 吗? @caesay,如果你不能重构你的代码来传递一个接口而不是你的基类,恐怕你的选择是有限的,但你可以使用某种事件中继,当一个域中的值发生变化时,该更改将转发给其他域中的同伴并在内部更新值。虽然这看起来很脆弱而且更复杂,但如果你不能重构,那么你需要一个共享的远程对象为你来回代理更改...... 如果它包含一个派生自 MarshalByRefObject 的内部类,它会在第二个 appdomain 中重新实例化还是被代理?可以在属性设置器上调用它,但我不确定如何让它更新其他 appdomain。 @caesay - 不,不幸的是,这行不通。当对象不是 MarshalByRefObject 派生时,它使用序列化,这本质上是“按值”,这意味着当实例跨域传递时,序列化“发送”域中的对象,然后(反序列化)创建类型的新实例“接收”域并继续设置每个属性。所以即使是内部引用也会以这种方式序列化。传递“by ref”的唯一方法是从 MarshalByRefObject 派生。我真的认为提取公共接口并进行重构符合您的最大利益。以上是关于在不同 AppDomain 上的序列化分类之间同步属性/字段的主要内容,如果未能解决你的问题,请参考以下文章
如何在两个 .NET AppDomain 之间传递未知类型?
为啥实体框架在不同的 AppDomain 中运行时会明显变慢?