c# - 使用 MarshalByRefObject 进行奇怪的 .NET 远程处理 SerializationException

标签 c# .net serialization remoting

我的应用程序中存在远程处理问题。由于架构相当复杂,我将尝试用虚拟名称制作一个简单的示例来说明问题。

考虑这些组件:

  • MyApp.Client.exe:客户端应用
  • MyApp.Service.exe:托管服务器的 Windows 服务
  • MyApp.Server.dll:服务器实现
  • MyApp.Shared.dll:包含通用接口(interface)和类型定义的共享库

在 MyApp.Shared.dll 中,我有这些接口(interface):

public interface IFoo
{
    ...
}

public interface IFooManager
{
    IList<IFoo> GetFooList();
    ...
}

这两个接口(interface)都在 MyApp.Server.dll 中实现为 MarshalByRefObjects:

class Foo : MarshalByRefObject, IFoo
{
    ...
}

class FooManager : MarshalByRefObject, IFooManager
{
    public IList<IFoo> GetFooList()
    {
        IList<IFoo> foos = new List<IFoo>();
        // populate the list with instances of Foo
        // ...
        return foos;
    }

    ...
}

在客户端,我有一个服务器上 FooManager 对象的代理实例。当我对其调用 GetFooList 时,我可以看到 FooManager.GetFooList() 方法已执行,但当它返回时,我得到以下 SerializationException:

Unable to find assembly 'MyApp.Server, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

Server stack trace: 
   at System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly()
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assemblyInfo, String name)
   at System.Runtime.Serialization.Formatters.Binary.ObjectMap..ctor(String objectName, String[] memberNames, BinaryTypeEnum[] binaryTypeEnumA, Object[] typeInformationA, Int32[] memberAssemIds, ObjectReader objectReader, Int32 objectId, BinaryAssemblyInfo assemblyInfo, SizedArray assemIdToAssemblyTable)
   at System.Runtime.Serialization.Formatters.Binary.ObjectMap.Create(String name, String[] memberNames, BinaryTypeEnum[] binaryTypeEnumA, Object[] typeInformationA, Int32[] memberAssemIds, ObjectReader objectReader, Int32 objectId, BinaryAssemblyInfo assemblyInfo, SizedArray assemIdToAssemblyTable)
   at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped record)
   at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryHeaderEnum binaryHeaderEnum)
   at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Remoting.Channels.CoreChannel.DeserializeBinaryResponseMessage(Stream inputStream, IMethodCallMessage reqMsg, Boolean bStrictBinding)
   at System.Runtime.Remoting.Channels.BinaryClientFormatterSink.DeserializeMessage(IMethodCallMessage mcm, ITransportHeaders headers, Stream stream)
   at System.Runtime.Remoting.Channels.BinaryClientFormatterSink.SyncProcessMessage(IMessage msg)

Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at MyApp.Shared.IFooManager.GetFooList()
   ...
   at MyApp.Client.ViewModel.MainWindowViewModel.LoadFoos()
   ...

所以我猜它正在尝试序列化 Foo 类(当 GetFooList 返回一个空列表时我没有得到异常)或在中使用的另一种类型。但为什么它会尝试序列化它呢?因为 Foo 是一个 MarshalByRefObject,它不应该返回一个 Foo 实例的代理吗?而且无论如何,IFoo 接口(interface)不会公开任何在 MyApp.Server.dll 中定义的类型的对象...

问题之前没有出现,因为所有程序集都在同一个目录中,所以 MyApp.Server.dll 可能已加载到客户端 AppDomain 中(这不应该发生)。但现在我正试图分离客户端和服务器组件,因此客户端不应该依赖于服务器端程序集...

有人知道发生了什么事吗?我怎样才能获得有关异常的更多详细信息(例如,它试图序列化哪种类型)?堆栈跟踪不是很有帮助...

最佳答案

我做了一个非常简单的应用程序,你是对的,在远程处理中,List 和 IFoo 都被编码,没有序列化发生。

首先我在 shared.dll 中创建了接口(interface)

namespace Shared
{
    public interface IFoo
    {
        string Name{get;set;}
    }

    public interface IFooMgr {
        IList<IFoo> GetList();
    }
}

然后我确实创建了一个 Foo 类、一个 Manager 并发布到远程处理:

namespace Server
{
    public class Foo : MarshalByRefObject, IFoo
    {
        public string Name
        {
            get;set;
        }
    }

    public class FooManager :  MarshalByRefObject, IFooMgr
    {
        public IList<IFoo> GetList()
        {
            IList<IFoo> fooList = new List<IFoo>();
            fooList.Add(new Foo { Name = "test" });
            fooList.Add(new Foo { Name = "test2" });
            return fooList;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ChannelServices.RegisterChannel(new TcpChannel(1237),true);
            System.Runtime.Remoting.RemotingServices.Marshal(new FooManager(),
               "FooManager");
            Console.Read();
        }
    }
}

最后,作为另一个控制台应用程序的客户端,在 appdomain 之外并位于另一个无法访问 server.exe 的文件夹中:

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            TcpChannel tcpChannel = new TcpChannel();
            ChannelServices.RegisterChannel(tcpChannel,true);
            Type requiredType = typeof(IFooMgr);
            IFooMgr remoteObject = (IFooMgr)Activator.GetObject(requiredType,
                "tcp://localhost:1237/FooManager");
            IList<IFoo> foos = remoteObject.GetList();
            foreach (IFoo foo in foos)
            {
                 Console.WriteLine("IsProxy:{0}, Name:{1}",
                      RemotingServices.IsTransparentProxy(foo), foo.Name);
            }
            Console.ReadLine();
        }
    }
}

并且如您预期的那样工作,管理器和 foo 对象都被编码,没有序列化,所以问题可能在您的代码中更深层次。


编辑: 如果您确定没有人创建过可序列化的 IFoo 类,如下所示:

[Serializable]
public class Foo2 : IFoo
{
    public string Name { get; set; }
}

然后,我唯一想到的是可能有一个 Surrogate为正在序列化它的类注册,而不是使用默认的 MBR 行为。

关于c# - 使用 MarshalByRefObject 进行奇怪的 .NET 远程处理 SerializationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3548456/

相关文章:

c# - 数据模板内的 UWP 网格

c# - 使用 .NET 的 SOA 架构真实示例

.net - .Net 和 iPhone 之间的三重 DES 互操作性?

c# - ListBox 项目在 OwnerDrawFixed 模式下不显示

java - 如何序列化 HttpHandler 类中的对象

c# - 如何告诉 AutoMapper 使用 "pass by reference?"

c# - 我应该实例化一个新的委托(delegate)吗?

c# - 如何安全地分块转换 `ToBase64String`?

php - 序列化和反序列化 $_POST 数组是否安全?

c# - 为什么当文件变大时我的内存就会耗尽?