2

我有一个使用 .NET 3.5 并通过 .NET 远程处理公开一些方法的服务。其中一些方法需要一个 DataSet 作为参数。当客户端也运行 .NET 3.5 时,一切都很好。但是,当客户端运行 .NET 4 时,我收到以下远程异常(在服务器上):

无法加载文件或程序集 'System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' 或其依赖项之一。该系统找不到指定的文件。

所以显然有两个版本的数据集 - 版本 2 和版本 4。我尝试创建一个单独的 .NET 3.5 项目,在该项目中创建数据集并将其发送到服务器。但是当它与其他 .NET 4 程序集一起加载到 .NET 4 运行时中时,它仍然使用 DataSet 版本 4。

最奇怪的是在 .net 3.5 中运行的不同服务我可以毫无问题地发送版本 4 数据集。我一直无法弄清楚那里有什么不同。

任何见解或解决方案将不胜感激。

更新: 微软似乎意识到了这个问题:见这里。非常可悲的是,如果真的需要这样的黑客来在不同的.NET版本之间进行通信......

4

1 回答 1

1

下面的代码实现了一个可以在客户端上使用的自定义 BinaryFormatterSink,而不是 BinaryClientFormatterSink。它基于MSDN上的 microsoft 示例

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;
        }
    }
}
于 2012-11-15T13:58:27.120 回答