开发者

Strange .NET remoting SerializationException with MarshalByRefObject

开发者 https://www.devze.com 2023-01-12 13:38 出处:网络
I\'m having a remoting issue in my application. Since the architecture is quite complex, I\'ll try to make an simple example with dummy names to illustrate the problem.

I'm having a remoting issue in my application. Since the architecture is quite complex, I'll try to make an simple example with dummy names to illustrate the problem.

Consider these components:

  • MyApp.Client.exe: client application
  • MyApp.Service.exe: Windows service that hosts the server
  • MyApp.Server.dll: server implementation
  • MyApp.Shared.dll: shared library containing common interface and type definitions

In MyApp.Shared.dll, I have these interfaces:

public interface IFoo
{
    ...
}

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

Both interfaces are implemented in MyApp.Server.dll as 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;
    }

    ...
}

On the client side, I have a proxy instance to the FooManager object on the server. When I call GetFooList on it, I can see that the FooManager.GetFooList() method is executed, but when it returns I get the following 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 isCross开发者_运维百科AppDomain, 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()
   ...

So I'm guessing it is trying to serialize the Foo class (I don't get an exception when GetFooList returns an empty list) or another type used in Foo. But why would it try to serialize it ? Since Foo is a MarshalByRefObject, shouldn't it return a proxy to the Foo instance ? And anyway, the IFoo interface doesn't expose any object of types defined in MyApp.Server.dll...

The problem didn't appear before because all assemblies were in the same directory, so MyApp.Server.dll was probably loaded in the client AppDomain (which isn't supposed to happen). But now I'm trying to separate the client and server components, so the client shouldn't depend on a server-side assembly...

Does anyone have any idea about what is going on ? And how could I get more details about the exception (e.g. which type is it trying to serialize) ? The stack trace is not very helpful...


I did very a simple application and you are right, in remoting both the List and the IFoo are marshaled, no serialization occurs.

First I created the interfaces in the shared.dll

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

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

Then I did create a Foo class, a Manager and published to remoting:

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();
        }
    }
}

And finally the client, as another console application, out of the appdomain and in another folder without access to the 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();
        }
    }
}

And worked as you expected, both the manager and the foo objects were marshaled, nothing serialized, so the problem may be deeper in your code.


Edit: If you are sure that nobody has created a serializable IFoo class, like this:

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

Then, the only thing that comes to my mind is that there might be a Surrogate registered for your class that was serializing it instead of using the default MBR behavior.


If you're getting a list of IFoos (Foo being the implementing class,) the binary serializer will attempt to serialize all the Foo objects. MarshalByRefObject supports proxy generation, which is different from serialization. Sending objects over the wire will require them to be serialized.

First and foremost, Foo must be marked with the [Serializable] attribute or implement ISerializable. All its members must also.

The error you're getting suggests that the server end can't find the assembly that defines the type. The easiest fix is to strongly-name the assembly that defines Foo and add it to the GAC on the server side.


This is the cornerstone of the Remoting: your Foo object can happily be MarshalByRefObject and used by Remoting but needs to be called by Remoting. You have created a channel to communicate with the FooManager and not the Foo. Remember all types passed to and from in a remoting session must be serializable.

Here is how I would do it: Have a GetAllFooIds() which return a list/array of IDs to Foo and then use GetFoo by passing the Foo id.

Update I think perhaps my statement above was not clear enough. The point is, an object is either serialisable or MarshalByRefObject. In your case, List<> is serialisable and as such cannot hold instance of MarshalByRefObject object. As I suggested, I will break the calls: one to get ids and another to get to the individual item. Slow, yes, but it is the only way I can think of to do it.

0

精彩评论

暂无评论...
验证码 换一张
取 消