我的应用程序中存在远程处理问题。由于架构相当复杂,我将尝试用虚拟名称制作一个简单的示例来说明问题。
考虑这些组件:
- 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/