【问题标题】:Synchronizing properties / fields between serialized classed on different AppDomains在不同 AppDomain 上的序列化分类之间同步属性/字段
【发布时间】:2013-06-24 04:20:21
【问题描述】:

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

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

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

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

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

有什么方法可以将第二个 appdomain 对类所做的更改更新回命令上的主 appdomain 字段,以便我可以“同步”字段的状态?

【问题讨论】:

    标签: c# serialization 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并实现IFooFoo包装类

    /// <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 派生。我真的认为提取公共接口并进行重构符合您的最大利益。
    猜你喜欢
    • 2018-09-23
    • 2011-07-17
    • 2021-02-22
    • 1970-01-01
    • 2019-11-02
    • 2012-10-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-04
    相关资源
    最近更新 更多