65.9K
CodeProject 正在变化。 阅读更多。
Home

一个示例 WCF 数据代理实现

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2016年11月29日

CPOL

5分钟阅读

viewsIcon

14217

downloadIcon

230

使用 WCF 数据合同代理的完整示例

引言

本文将展示如何修改使用 DataContractSerializer 的选定类型的序列化方法。可下载的源代码包含一个完整的代理实现和使用示例。该示例代理使用 BinaryFormatter 序列化选定的类。例如,如果您想通过 WCF 传输更大的对象,并且不想更改序列化器,您可以使用此 BinarySurrogate 来减小 XML 的大小。这只是一个示例实现,如果您不想使用 BinaryFormatter,可以很容易地更改为使用其他序列化方式。(例如,protobuf、自定义序列化器)。

背景

了解数据契约代理的基本知识非常重要。请先阅读 MSDN。 

该示例包含两个类库项目,一个 WCF 服务应用程序,一个控制台客户端。您应该能够生成并运行这些项目,并配置 WCF 消息日志,以便检查代理的结果。

使用代码

解决方案包含四个项目

  • WcfSurrogateTutorial.Surrogate:这是一个类库项目。仅包含进行代理的类和接口,没有任何业务逻辑的具体知识。
  • WcfSurrogateTutorial.Common:这是一个类库,包含“业务逻辑”对象和其他通用类。例如,类型提供程序。
  • WcfSurrogateTutorial.TestServer:这是一个 WCF 服务库项目,包含返回“业务逻辑”对象的示例服务协定。
  • WcfSurrogateTutorial.TestClient:这是一个控制台应用程序,包含对 TestServer 的服务引用。

使用数据契约代理,您可以在序列化过程中更改类的类型。我们将手动序列化对象,并将序列化后的字符串存储在一个通用类中。首先,我们创建这个类,它将替代所有选定的类型。这就是 BinaryStringContainer,它只有一个属性,即 BinaryString。每个被替代的类都将使用二进制格式化程序进行序列化,并将结果字符串存储在此属性中。然后,WCF 将使用 DataContractSerializer 序列化这个类,而无需使用 DataContractSerializer 序列化完整的对象层次结构。

    [DataContract]
    public class BinaryStringContainer
    {
        private string binaryString = null;
        [DataMember]
        public string BinaryString
        {
            get
            { 
                return binaryString; 
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("BinaryString");
                }

                binaryString = value;
            }
        }
    }

编写自己的代理的第一步是实现 IDataContractSurrogate 接口。此接口包含三个对我们很重要的重要方法:

  • GetDataContractType:获取当前类型并返回序列化后的类型。在我们的例子中,它会返回 BinaryStringContainer 类型,用于所有我们想要使用 BinaryFormatter 进行序列化的类型。对于其他类型,它返回它接收到的类型。
  • GetObjectToSerialize:获取对象并将其转换为序列化后的类型。如果序列化后的类型是 BinaryStringContainer ,它将使用 BinaryFormatter 序列化对象,并返回一个带有序列化字符串的新实例。
  • GetDeserializedObject:获取序列化后的对象并转换回。如果序列化后的对象是 BinaryStringContainer,它将使用 BinaryFormatter 反序列化对象并返回原始对象。

我们不需要实现接口的其他方法。如果其中任何一个被调用,我们将抛出不支持的异常。我们的 BinarySurrogate 类包含此实现。

        public Type GetDataContractType(Type type)
        {
            if (surrogatedTypes.Contains(type))
            {
                return typeof(BinaryStringContainer);
            }
            else
            {
                return type;
            }
        }

        public object GetObjectToSerialize(object obj, Type targetType)
        {
            if (targetType == typeof(BinaryStringContainer))
            {
                BinaryStringContainer bs = surrogateSerializer.ConvertToSurrogate(obj);
                return bs;
            }
            else
            {
                return obj;
            }
        }

        public object GetDeserializedObject(object obj, Type targetType)
        {
            BinaryStringContainer bs = obj as BinaryStringContainer;
            if (bs != null)
            {
                object ret = surrogateSerializer.ConvertFromSurrogate(bs);
                return ret;
            }
            else
            {
                return obj;
            }
        }

为了使此实现更灵活,此 BinarySurrogate 通过构造函数使用两个依赖项进行配置。IBinarySerializedTypeProvider 仅包含一个方法,该方法返回我们想要代理的类型。ISurrogateConverter 接口是对序列化的抽象。它的两个方法用于将对象转换为 BinaryStringContainer 和反之。我们实现了一个 BinarySurrogateConverter 类,它使用 BinaryFormatter 将对象序列化为字符串。这意味着所有被代理的类现在都应该被标记为 Serializable

    public class BinarySurrogateConverter : ISurrogateConverter
    {
        public BinaryStringContainer ConvertToSurrogate(object obj)
        {
            BinaryStringContainer ret = new BinaryStringContainer();
            if (obj == null)
            {
                return ret;
            }

            using (MemoryStream ms = new MemoryStream())
            {
                IFormatter formatter = new BinaryFormatter();
                formatter.Serialize(ms, obj);
                var ba = ms.ToArray();
                ret.BinaryString = Convert.ToBase64String(ba);
            }
            return ret;
        }

        public object ConvertFromSurrogate(BinaryStringContainer bs)
        {
            if (bs.BinaryString == null)
            {
                return null;
            }

            object ret;
            byte[] ba = Convert.FromBase64String(bs.BinaryString);

            using (MemoryStream ms = new MemoryStream(ba))
            {
                IFormatter formatter = new BinaryFormatter();
                ret = formatter.Deserialize(ms);
            }

            return ret;
        }

类型提供程序接口在 Surrogate 项目中没有实现,因为它应该知道具体的类。因此,我们在 Common 项目中创建了一个类型转换器提供程序类。正如您所见,它只返回 SurrogatedData 类型,我们只想代理这个类型,而保持其他类型不变。

    public class TestSurrogateTypeConverter : IBinarySerializedTypeProvider
    {
        private static readonly List<Type> types = new List<Type> { typeof(SurrogatedData) };

        public IEnumerable<Type> GetBinarySerializableTypes()
        {
            return types;
        }
    }

下一步是在您的终结点应用此代理。我们创建了一个可以附加到 ServiceContracts 接口的 Attribute 类。此 Attribute 遍历所有 Operation,通过设置 DataContractSerializerOperationBehavior 类的 DataContractSurrogate 属性来应用代理。

            foreach (OperationDescription od in contract.Operations)
            {
                DataContractSerializerOperationBehavior dataContractBehavior = od.Behaviors.Find<DataContractSerializerOperationBehavior>() as DataContractSerializerOperationBehavior;
                if (dataContractBehavior != null)
                {
                    dataContractBehavior.DataContractSurrogate = new BinarySurrogate(typeProvider, surrogateConverter);
                }
                else
                {
                    dataContractBehavior = new DataContractSerializerOperationBehavior(od);
                    dataContractBehavior.DataContractSurrogate = new BinarySurrogate(typeProvider, surrogateConverter);
                    od.Behaviors.Add(dataContractBehavior);
                }
            }

在服务器端,您只需要将此 Attribute 应用于 ISurrogateTestService 服务协定接口。服务器会将 SurrogatedData 类序列化为 BinaryStringContainer 类型。

    [ServiceContract]
    [UseBinarySurrogateBehavior(typeof(TestSurrogateTypeConverter), typeof(BinarySurrogateConverter))]
    public interface ISurrogateTestService
    {
        [OperationContract]
        SurrogatedData GetSurrogatedData();

        [OperationContract]
        NonSurrogatedData GetNonSurrogatedData();
    }

在客户端,我们应该应用此代理类以正确反序列化对象。为了反序列化对象,您应该在客户端程序集中拥有具体类型的引用。如果您使用 Visual Studio 生成代理类,您应该检查该程序集的重用选项。否则,BinaryFormatter 反序列化将失败,因为对象定义在未引用的程序集中。在客户端,您不能使用 Attribute,因此必须手动应用代理。但方法与服务器端相同。请参阅 ApplyDataContractSurrogate 方法。 

现在您可以启动 WCF 服务并运行客户端控制台应用程序。控制台应用程序首先获取代理数据,然后获取未代理的数据。这两种数据都应该正常工作,代理是透明的。如果您想检查差异,应该记录 WCF 消息。

代理响应仅包含 BinaryString,它是对象的 base64 编码字符串表示。

<MessageLogTraceRecord Time="2016-03-31T17:02:49.8664839+02:00" Source="TransportSend" Type="System.ServiceModel.Dispatcher.OperationFormatter+OperationFormatterMessage" xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
<Addressing>
<Action>http://tempuri.org/ISurrogateTestService/GetSurrogatedDataResponse</Action>
</Addressing>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetSurrogatedDataResponse xmlns="http://tempuri.org/">
<GetSurrogatedDataResult xmlns:a="http://schemas.datacontract.org/2004/07/WcfSurrogateTutorial.Surrogate" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:BinaryString>AAEAAAD/////AQAAAAAAAAAMAgAAAFJXY2ZTdXJyb2dhdGVUdXRvcmlhbC5Db21tb24sIFZlcnNpb249MS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsBQEAAAAuV2NmU3Vycm9nYXRlVHV0b3JpYWwuVGVzdF...</a:BinaryString>
</GetSurrogatedDataResult>
</GetSurrogatedDataResponse>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>

未代理的对象看起来就像其他任何正常的 DataContract 序列化对象。它包含所有属性和列表项作为 XML 元素。

<MessageLogTraceRecord Time="2016-03-31T17:02:50.2063213+02:00" Source="TransportSend" Type="System.ServiceModel.Dispatcher.OperationFormatter+OperationFormatterMessage" xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
<Addressing>
<Action>http://tempuri.org/ISurrogateTestService/GetNonSurrogatedDataResponse</Action>
</Addressing>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetNonSurrogatedDataResponse xmlns="http://tempuri.org/">
<GetNonSurrogatedDataResult xmlns:a="http://schemas.datacontract.org/2004/07/WcfSurrogateTutorial.TestServer" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Id>8669505</a:Id>
<a:ListValue xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<b:string>7568d8e1-6431-4f39-9238-a5948a3bf9e0</b:string>
<b:string>7a53e6e1-fbb7-4ef5-bd6c-aa89c220dd8a</b:string>
<b:string>a82de840-3dff-4586-8cb3-c338d68fa782</b:string>
.
.
.
<b:string>aa2252dd-4952-463c-ac33-973f46f92f90</b:string>
</a:ListValue>
<a:StringValue>Non surrogated data</a:StringValue>
</GetNonSurrogatedDataResult>
</GetNonSurrogatedDataResponse>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>

关注点

在我们的项目中,我们使用这种代理来提高序列化性能。我们不想改变 WCF 序列化器,但我们希望更快地序列化大型对象层次结构。我们为不同的类型使用 BinaryFormatter、protobuf 和自定义序列化器。

历史

在此处保持您所做的任何更改或改进的实时更新。

© . All rights reserved.