Question

I have a service that uses .NET 3.5 and exposes some methods via .NET remoting. Some of those methods expect a DataSet as an argument. When the client is also running .NET 3.5 everything is fine. However, when the client is running .NET 4 I get the following remote exception (on the server):

Could not load file or assembly 'System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' or one of its dependencies. The system cannot find the file specified.

So apparently there are two version of DataSets - version 2 and version 4. I have tried creating a separate .NET 3.5 project where the DataSet is created and sent to the server. But when it is loaded in the .NET 4 runtime along with other .NET 4 assemblies, it still uses DataSet version 4.

The weirdest thing is with a different service also running in .net 3.5 I can send version 4 DataSets with no problems. I have not been able to figure out what is different there.

Any insight or solutions would be greatly appreciated.

Update: Microsoft seems to be aware of the problem: See here. Pretty sad though if such a hack is really required to communicate between different .NET versions...

Était-ce utile?

La solution

The below code implements a custom BinaryFormatterSink that can be used on the client instead of the BinaryClientFormatterSink. It is based on the microsoft example at MSDN

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace bla
{
    class VersionConversionClientSinkProvider : IClientChannelSinkProvider
    {
        public IClientChannelSink CreateSink(IChannelSender channel, string url, object remoteChannelData)
        {
            if (Next != null )
            {
                var nextSink = Next.CreateSink(channel, url, remoteChannelData);
                if (nextSink != null)
                {
                    return new VersionConversionClientSink(nextSink);  
                }
            }
            return null;
        }

        public IClientChannelSinkProvider Next { get; set; }
    }

    class VersionConversionClientSink : IClientChannelSink, IMessageSink
    {
        public VersionConversionClientSink(IClientChannelSink channel)
        {
            _next = channel;
        }

        readonly IClientChannelSink _next;

        public IDictionary Properties
        {
            get { return NextChannelSink.Properties; }
        }

        public void ProcessMessage(IMessage msg, ITransportHeaders requestHeaders, Stream requestStream, out ITransportHeaders responseHeaders, out Stream responseStream)
        {
            throw new NotSupportedException();
        }

        private void SerializeMessage(IMessage msg, out ITransportHeaders headers, out Stream stream)
        {
            var binaryFormatter = new BinaryFormatter
                {
                    Binder = new VersionConversionSerializationBinder()
                };

            stream = new MemoryStream();
            binaryFormatter.Serialize(stream, msg);
            stream.Position = 0;

            headers = new TransportHeaders();
        }

        private IMessage DeserializeMessage(Stream stream)
        {
            var binaryFormatter = new BinaryFormatter
                {
                    AssemblyFormat = FormatterAssemblyStyle.Simple
                };

            var msg = (IMessage) binaryFormatter.Deserialize(stream);
            stream.Close();
            return msg;
        }

        public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, IMessage msg, ITransportHeaders headers, Stream stream)
        {
            throw new NotSupportedException();
        }

        public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack, object state, ITransportHeaders headers, Stream stream)
        {
            sinkStack.AsyncProcessResponse(headers, stream);
        }

        public Stream GetRequestStream(IMessage msg, ITransportHeaders headers)
        {
            return _next.GetRequestStream(msg, headers);
        }

        public IClientChannelSink NextChannelSink
        {
            get { return _next; }
        }

        public IMessage SyncProcessMessage(IMessage msg)
        {
            IMethodCallMessage mcm = msg as IMethodCallMessage;
            try
            {
                ITransportHeaders headers;
                Stream stream;
                SerializeMessage(msg, out headers, out stream);
                ITransportHeaders responseHeaders;
                Stream responseStream;
                NextChannelSink.ProcessMessage(msg, headers, stream, out responseHeaders, out responseStream);
                if (responseHeaders == null)
                    throw new ArgumentNullException("returnHeaders");
                else
                    return DeserializeMessage(responseStream);
            }
            catch (Exception ex)
            {
                return new ReturnMessage(ex, mcm);
            }
        }

        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            var mcm = (IMethodCallMessage)msg;
            try
            {
                ITransportHeaders headers;
                Stream stream;
                SerializeMessage(msg, out headers, out stream);
                var channelSinkStack = new ClientChannelSinkStack(replySink);
                channelSinkStack.Push(this, msg);
                NextChannelSink.AsyncProcessRequest(channelSinkStack, msg, headers, stream);
            }
            catch (Exception ex)
            {
                IMessage msg1 = new ReturnMessage(ex, mcm);
                if (replySink != null)
                    replySink.SyncProcessMessage(msg1);
            }
            return null;
        }

        public IMessageSink NextSink
        {
            get { throw new NotSupportedException(); }
        }
    }

    class VersionConversionSerializationBinder : SerializationBinder
    {
        string[] frameworkPublicKeyTokens = new string[] {
                "B7-7A-5C-56-19-34-E0-89",
                "B0-3F-5F-7F-11-D5-0A-3A",
                "31-BF-38-56-AD-36-4E-35",
                "89-84-5D-CD-80-80-CC-91"
            };

        bool IsFrameworkAssembly(Assembly assembly)
        {
            foreach (string frameworkToken in frameworkPublicKeyTokens)
            {
                if (frameworkToken == BitConverter.ToString(assembly.GetName().GetPublicKeyToken()))
                {
                    return true;
                }
            }
            return false;
        }

        public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
        {
            // To handle arrays
            if (serializedType.IsArray)
            {
                string elementTypeName;
                Type elementType = serializedType.GetElementType();
                BindToName(elementType, out assemblyName, out elementTypeName);
                StringBuilder typeNameBuilder = new StringBuilder(elementTypeName);
                typeNameBuilder.Append("[");
                int arrayRank = serializedType.GetArrayRank();
                for (int i = 1; i < arrayRank; i++)
                {
                    typeNameBuilder.Append(",");
                }
                if (arrayRank == 1 && serializedType == elementType.MakeArrayType(1))
                {
                    typeNameBuilder.Append("*");
                }
                typeNameBuilder.Append("]");
                typeName = typeNameBuilder.ToString();
            }
            // To handle generic types
            else if (serializedType.IsGenericType && !serializedType.IsGenericTypeDefinition)
            {
                string definitionTypeName;
                Type[] genericParameters = serializedType.GetGenericArguments();
                BindToName(serializedType.GetGenericTypeDefinition(), out assemblyName, out definitionTypeName);
                StringBuilder typeNameBuilder = new StringBuilder(definitionTypeName);
                typeNameBuilder.Append("[");
                for (int i = 0; i < genericParameters.Length; i++)
                {
                    if (i > 0)
                    {
                        typeNameBuilder.Append(",");
                    }
                    string parameterTypeName, parameterAssemblyName;
                    BindToName(genericParameters[i], out parameterAssemblyName, out parameterTypeName);
                    typeNameBuilder.AppendFormat("[{0}, {1}]", parameterTypeName, parameterAssemblyName);
                }
                typeNameBuilder.Append("]");
                typeName = typeNameBuilder.ToString();
            }
            // To handle the rest of types
            else
            {
                assemblyName = serializedType.Assembly.FullName;
                if (IsFrameworkAssembly(serializedType.Assembly))
                {
                    assemblyName = assemblyName.Replace("Version=4.0.0.0", "Version=2.0.0.0");
                }
                typeName = serializedType.FullName;
            }
        }

        public override Type BindToType(string assemblyName, string typeName)
        {
            return null;
        }
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top