我的申请中有一个远程问题。由于架构非常复杂,我将尝试使用虚拟名称来举例说明问题。
考虑以下组件:
在MyApp.Shared.dll中,我有这些接口:
public interface IFoo
{
...
}
public interface IFooManager
{
IList<IFoo> GetFooList();
...
}
这两个接口都在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
中使用的其他类型。但为什么要尝试序列化呢?由于Foo
是MarshalByRefObject
,它不应该返回Foo
实例的代理吗?无论如何,IFoo
接口不会暴露MyApp.Server.dll中定义的任何类型的对象......
此问题之前没有出现,因为所有程序集都在同一目录中,因此MyApp.Server.dll可能已加载到客户端AppDomain中(不应该发生)。但现在我正在尝试分离客户端和服务器组件,因此客户端不应该依赖于服务器端组件...
有没有人知道发生了什么?我怎样才能获得有关异常的更多细节(例如,它尝试序列化的类型)?堆栈跟踪不是很有用......
答案 0 :(得分:1)
我做了一个非常简单的应用程序而你是对的,在远程处理List和IFoo都被编组,没有序列化发生。
首先我在shared.dll
中创建了接口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行为。
答案 1 :(得分:0)
如果您获得IFoos
(Foo
作为实现类)的列表,则二进制序列化程序将尝试序列化所有Foo
个对象。 MarshalByRefObject
支持代理生成,这与序列化不同。通过线路发送对象将需要序列化它们。
首先,Foo
必须使用[Serializable]
属性标记或实施ISerializable.
其所有成员也必须。{/ p>
您收到的错误表明服务器端无法找到定义类型的程序集。最简单的解决方法是强制命名定义Foo
的程序集,并将其添加到服务器端的GAC。
答案 2 :(得分:0)
这是Remoting的基石:你的Foo
对象可以很乐意MarshalByRefObject
并被Remoting使用,但需要通过Remoting调用。您已创建了与FooManager
而非Foo
进行通信的渠道。 请记住,在远程会话中传入和传出的所有类型都必须是可序列化的。
我将如何做到这一点: 有一个GetAllFooIds(),它返回一个ID列表/数组给Foo,然后通过传递Foo id来使用GetFoo。
<强>更新强> 我想也许我上面的陈述不够明确。关键是,对象是可序列化的或MarshalByRefObject。在您的情况下,List&lt;&gt;是可序列化的,因此不能保存MarshalByRefObject对象的实例。正如我所建议的那样,我将打破电话:一个获取ID,另一个获取单个项目。慢,是的,但这是我能想到的唯一方法。