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

时间:2013-06-24 04:20:21

标签: c# serialization appdomain

所以情况就是这样 - 我有一个派生自抽象基类的类(因此不能派生MarshalByRefObject并用作代理对象)并且它具有正确的状态是很重要的,因为它正在处理用流和做一些重要的事情。

它标记为Serializable,因为我需要将其传递到另一个AppDomain并让该appdomain定期调用.Post(string)方法。

我不知道有关appdomains的一切,但我看到一些奇怪的事情,我假设该类实际上在第二个AppDomain中使用相同的字段和属性重新实例化,然后我最终得到了多个实例该课程。

这是一个问题,因为当第二个AppDomain调用.Post()时,它会对流进行修改,并且它自己的对象状态,但主应用程序域中类中包含的私有变量不会更新 - 所以主应用程序域中存在损坏的状态,一旦主应用程序域在其自己的类版本上调用.Post,我就会遇到损坏的流!

我也不能使用包装类,因为我需要传递作为其抽象基类的对象,第二个appdomain不知道派生类的类型是什么,只知道调用{ {1}}这是在基类中定义的抽象方法。

有没有办法可以将第二个appdomain对该类所做的更改更新回命令的主appdomain字段,以便我可以“同步”字段的状态,这样可以说呢?

1 个答案:

答案 0 :(得分: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);
}

创建一个Foo包装类,该类派生自MarshalByRefObject并实现IFoo

/// <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返回到其他域并像往常一样调用IFooPost方法。

执行该程序的输出:

enter image description here