从零开始学WCF(5)--数据协定
1.数据协定概述
- 默认情况下,WCF使用称为数据协定序列化程序的序列化引擎对数据进行序列化和反序列化(与XML进行相互转换)。
- 所有.Net Framework基元类型(如整型和字符串型)以及某些被视为基元的类型(如DateTime和XmlElement)无需做其他任何准备工作就可序列化并被视为拥有默认数据协定。
- 必须为所创建的新复杂类型定义数据协定以便可以进行序列化。
- 正常情况下加DataContractAttribute应用到该类型来完成该任务。
- 可以将此属性应用到类,结构,枚举。
- 然后必须将DataMemberAttribute属性应用到数据
- 数据协定是使用"选择性加入“编程模型设计的;未用DataMemberAttribute属性显示标记的任何内容均不会序列化。
- 可以将DataMemberAttribute属性应用于字段,属性,事件。
- 成员可访问性级别(internal,private,protected,public)对数据协定无任何影响。
- 如果将DataMemberAttribute属性应用于静态成员,则忽略该属性。
- 在序列化期间,为属性数据成员调用get代码来获取要序列化的属性的值。
- 在反序列化期间,为属性数据成员调用Set代码,将属性设置为要反序列化的值。
- 对于将要生效的数据协定,它必须能序列化其所有数据成员,泛型类型的处理方式与非泛型类型完全相同。泛型参数无特殊要求。
Server Side:
// Copyright (c) Microsoft Corporation. All Rights Reserved. using System; using System.ServiceModel; using System.Runtime.Serialization; namespace Microsoft.ServiceModel.Samples { // Define a service contract. [ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples")] public interface ICalculator { [OperationContract] double Add(double n1, double n2); [OperationContract] double Subtract(double n1, double n2); [OperationContract] double Multiply(double n1, double n2); [OperationContract] double Divide(double n1, double n2); [OperationContract] MyData TestMethod(MyData mydata); } // Service class which implements the service contract. public class CalculatorService : ICalculator { public double Add(double n1, double n2) { return n1 + n2; } public double Subtract(double n1, double n2) { return n1 - n2; } public double Multiply(double n1, double n2) { return n1 * n2; } public double Divide(double n1, double n2) { return n1 / n2; } public MyData TestMethod(MyData mydata) { return new MyData(mydata.FirstName + "+111", mydata.lastName + "+222"); } } [DataContract(Namespace = "http://Microsoft.ServiceModel.Samples")] public class MyData { public MyData(string _firstName,string _lastName) { this.firstName = _firstName; this.lastName = _lastName; } string firstName; [DataMember(IsRequired=true)] public string lastName; [DataMember] public string FirstName { get { return firstName; } set { firstName = value; } } } }
ClientSide
//------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // Runtime Version:2.0.50727.4952 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ [assembly: System.Runtime.Serialization.ContractNamespaceAttribute("http://Microsoft.ServiceModel.Samples", ClrNamespace="microsoft.servicemodel.samples")] namespace microsoft.servicemodel.samples { using System.Runtime.Serialization; [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")] [System.Runtime.Serialization.DataContractAttribute(Name="MyData", Namespace="http://Microsoft.ServiceModel.Samples")] public partial class MyData : object, System.Runtime.Serialization.IExtensibleDataObject { private System.Runtime.Serialization.ExtensionDataObject extensionDataField; private string FirstNameField; private string lastNameField; public System.Runtime.Serialization.ExtensionDataObject ExtensionData { get { return this.extensionDataField; } set { this.extensionDataField = value; } } [System.Runtime.Serialization.DataMemberAttribute()] public string FirstName { get { return this.FirstNameField; } set { this.FirstNameField = value; } } [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)] public string lastName { get { return this.lastNameField; } set { this.lastNameField = value; } } } } [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] [System.ServiceModel.ServiceContractAttribute(Namespace="http://Microsoft.ServiceModel.Samples", ConfigurationName="ICalculator")] public interface ICalculator { [System.ServiceModel.OperationContractAttribute(Action="http://Microsoft.ServiceModel.Samples/ICalculator/Add", ReplyAction="http://Microsoft.ServiceModel.Samples/ICalculator/AddResponse")] double Add(double n1, double n2); [System.ServiceModel.OperationContractAttribute(Action="http://Microsoft.ServiceModel.Samples/ICalculator/Subtract", ReplyAction="http://Microsoft.ServiceModel.Samples/ICalculator/SubtractResponse")] double Subtract(double n1, double n2); [System.ServiceModel.OperationContractAttribute(Action="http://Microsoft.ServiceModel.Samples/ICalculator/Multiply", ReplyAction="http://Microsoft.ServiceModel.Samples/ICalculator/MultiplyResponse")] double Multiply(double n1, double n2); [System.ServiceModel.OperationContractAttribute(Action="http://Microsoft.ServiceModel.Samples/ICalculator/Divide", ReplyAction="http://Microsoft.ServiceModel.Samples/ICalculator/DivideResponse")] double Divide(double n1, double n2); [System.ServiceModel.OperationContractAttribute(Action="http://Microsoft.ServiceModel.Samples/ICalculator/TestMethod", ReplyAction="http://Microsoft.ServiceModel.Samples/ICalculator/TestMethodResponse")] microsoft.servicemodel.samples.MyData TestMethod(microsoft.servicemodel.samples.MyData mydata); } [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] public interface ICalculatorChannel : ICalculator, System.ServiceModel.IClientChannel { } [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] public partial class CalculatorClient : System.ServiceModel.ClientBase<ICalculator>, ICalculator { public CalculatorClient() { } public CalculatorClient(string endpointConfigurationName) : base(endpointConfigurationName) { } public CalculatorClient(string endpointConfigurationName, string remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public CalculatorClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public CalculatorClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : base(binding, remoteAddress) { } public double Add(double n1, double n2) { return base.Channel.Add(n1, n2); } public double Subtract(double n1, double n2) { return base.Channel.Subtract(n1, n2); } public double Multiply(double n1, double n2) { return base.Channel.Multiply(n1, n2); } public double Divide(double n1, double n2) { return base.Channel.Divide(n1, n2); } public microsoft.servicemodel.samples.MyData TestMethod(microsoft.servicemodel.samples.MyData mydata) { return base.Channel.TestMethod(mydata); } }
// Copyright (c) Microsoft Corporation. All Rights Reserved. using System; using System.ServiceModel; using microsoft.servicemodel.samples; namespace Microsoft.ServiceModel.Samples { //The service contract is defined in generatedClient.cs, generated from the service by the svcutil tool. //Client implementation code. class Client { static void Main() { // Create a client CalculatorClient client = new CalculatorClient(); // Call the Add service operation. double value1 = 100.00D; double value2 = 15.99D; double result = client.Add(value1, value2); Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result); // Call the Subtract service operation. value1 = 145.00D; value2 = 76.54D; result = client.Subtract(value1, value2); Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result); // Call the Multiply service operation. value1 = 9.00D; value2 = 81.25D; result = client.Multiply(value1, value2); Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result); // Call the Divide service operation. value1 = 22.00D; value2 = 7.00D; result = client.Divide(value1, value2); Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result); MyData md = new MyData(); md.FirstName = "wang"; md.lastName = "xiaoming"; MyData newMD = client.TestMethod(md); Console.WriteLine("after format:FirstName:{0},LastName:{1}",newMD.FirstName,newMD.lastName); //Closing the client gracefully closes the connection and cleans up resources client.Close(); Console.WriteLine(); Console.WriteLine("Press <ENTER> to terminate client."); Console.ReadLine(); } } }
2.数据协定名称
- 有时客户端的服务不共享相同的类型。但只要两端的数据协定是等同的,客户端和服务仍可以互相传递数据。
- 完全限定的数据协定名称由命名空间和名称组成
- 数据成员只有名称,而没有命名空间。
- 处理数据协定时,WCF基础结构对于命名空间以及数据协定和数据成员的名称区分大小写。
- 数据协定命名空间
- 数据协定命名空间采用统一资源标示符(URI)的形式
- 设置DataContractAttribute的NameSpace属性。
- 数据成员名称
- 给定字段或属性的默认数据成员名称是该字段或属性的名称。
- 若要重写默认值,请将DataMemberAttribute的Name属性设置为其他值。
3.数据协定的等效性
- 要使数据协定等效,其命名空间和名称必须相同。此外,某一端上的每个数据成员还必须在另一端上具有等效的数据成员。
- 要使数据成员等效,其名称必须相同。此外,他们还必须表示同一类型的数据,也就是说,其数据协定必须等效。
- 如果同一端(发送方或接收方)存在两种类型,而其数据类型又不等效(例如,他们的数据成员不同),则不应为他们指定相同的名称和命名空间。否则,可能会发生异常。
4. 数据成员顺序和数据协定等效性
使用DataMemberAttribute类的Order属性可能会影响数据协定等效性。其成员必须以相同顺序出现,数据协定才能等效。默认顺序是按字母顺序。
5.数据排序的基本规则包括:
- 如果数据协定类型是继承层次结构的一部分,则其基类型的数据成员始终排在第一位。
- 排在下一位的是当前类型的数据成员(按字母顺序排列)这些成员未设置DatamemberAttribute属性的order属性的任何数据成员。这些成员首先按Order属性排序,如果多个成员具有特定的Order值,则按字母顺序排列。可以跳过Order值。
- 再下面是设置了DataMemberAttribute属性的Order属性的任何数据成员。这些成员首先按Order属性的值排序,如果多个成员具有特定的Order值,则按字母顺序排列。可以跳过Order值。
4.数据协定已知类型
- 发送的数据协定源自预期的数据协定
- 要传送的信息的声明类型是接口,而非类结构和枚举。
- 要传输的信息的类型是Object
- 有些类型(包括.net framework)具有属于上述三种类别之一的成员。例如,HashTable使用Object在哈希表中存储实际对象
KnownTypeAttribute类
通过首先检查传入消息选择为反序列化而实例化的类型,以确定消息内容遵循的数据协定。然后反序列化引擎尝试查找实现与消息内容兼容的数据协定的CLR类型。反序列化引擎在此过程中允许的候选类型集称为反序列化程序的”已知类型“集。
ServerSide:
// Copyright (c) Microsoft Corporation. All Rights Reserved. using System; using System.ServiceModel; using System.Runtime.Serialization; namespace Microsoft.ServiceModel.Samples { // Define a service contract. [ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples")] public interface IDataContractCalculator { [OperationContract] ComplexNumber Add(ComplexNumber n1, ComplexNumber n2); [OperationContract] ComplexNumber Subtract(ComplexNumber n1, ComplexNumber n2); [OperationContract] ComplexNumber Multiply(ComplexNumber n1, ComplexNumber n2); [OperationContract] ComplexNumber Divide(ComplexNumber n1, ComplexNumber n2); } // Define the data contract for a complex number [DataContract(Namespace="http://Microsoft.ServiceModel.Samples")] //Indicate that ComplexNumberWithMagnitude may be used in place of ComplexNumber [KnownType(typeof(ComplexNumberWithMagnitude))] public class ComplexNumber { [DataMember] private double real; [DataMember] private double imaginary; public ComplexNumber(double r1, double i1) { this.Real = r1; this.Imaginary = i1; } public double Real { get { return real; } set { real = value; } } public double Imaginary { get { return imaginary; } set { imaginary = value; } } } //Define the data contract for ComplexNumberWithMagnitude [DataContract(Namespace="http://Microsoft.ServiceModel.Samples")] public class ComplexNumberWithMagnitude : ComplexNumber { public ComplexNumberWithMagnitude(double real, double imaginary) : base(real, imaginary) { } [DataMember] public double Magnitude { get { return Math.Sqrt(Imaginary*Imaginary + Real*Real); } set { throw new NotImplementedException(); } } } // Service class which implements the service contract. public class DataContractCalculatorService : IDataContractCalculator { public ComplexNumber Add(ComplexNumber n1, ComplexNumber n2) { //Return the derived type return new ComplexNumberWithMagnitude(n1.Real + n2.Real, n1.Imaginary + n2.Imaginary); } public ComplexNumber Subtract(ComplexNumber n1, ComplexNumber n2) { //Return the derived type return new ComplexNumberWithMagnitude(n1.Real - n2.Real, n1.Imaginary - n2.Imaginary); } public ComplexNumber Multiply(ComplexNumber n1, ComplexNumber n2) { double real1 = n1.Real * n2.Real; double imaginary1 = n1.Real * n2.Imaginary; double imaginary2 = n2.Real * n1.Imaginary; double real2 = n1.Imaginary * n2.Imaginary * -1; //Return the base type return new ComplexNumber(real1 + real2, imaginary1 + imaginary2); } public ComplexNumber Divide(ComplexNumber n1, ComplexNumber n2) { ComplexNumber conjugate = new ComplexNumber(n2.Real, -1*n2.Imaginary); ComplexNumber numerator = Multiply(n1, conjugate); ComplexNumber denominator = Multiply(n2, conjugate); //Return the base type return new ComplexNumber(numerator.Real / denominator.Real, numerator.Imaginary); } } }
ClientSide:
// Copyright (c) Microsoft Corporation. All Rights Reserved. using System; using System.ServiceModel; namespace Microsoft.ServiceModel.Samples { //The service contract is defined in generatedClient.cs, generated from the service by the svcutil tool. //Client implementation code. class Client { static void Main() { // Create a client DataContractCalculatorClient client = new DataContractCalculatorClient(); // Call the Add service operation. ComplexNumber value1 = new ComplexNumber(); value1.real = 1; value1.imaginary = 2; ComplexNumber value2 = new ComplexNumber(); value2.real = 3; value2.imaginary = 4; ComplexNumber result = client.Add(value1, value2); Console.WriteLine("Add({0} + {1}i, {2} + {3}i) = {4} + {5}i", value1.real, value1.imaginary, value2.real, value2.imaginary, result.real, result.imaginary); if (result is ComplexNumberWithMagnitude) { Console.WriteLine("Magnitude: {0}", ((ComplexNumberWithMagnitude)result).Magnitude); } else { Console.WriteLine("No magnitude was sent from the service"); } // Call the Subtract service operation. value1 = new ComplexNumber(); value1.real = 1; value1.imaginary = 2; value2 = new ComplexNumber(); value2.real = 3; value2.imaginary = 4; result = client.Subtract(value1, value2); Console.WriteLine("Subtract({0} + {1}i, {2} + {3}i) = {4} + {5}i", value1.real, value1.imaginary, value2.real, value2.imaginary, result.real, result.imaginary); if (result is ComplexNumberWithMagnitude) { Console.WriteLine("Magnitude: {0}", ((ComplexNumberWithMagnitude)result).Magnitude); } else { Console.WriteLine("No magnitude was sent from the service"); } // Call the Multiply service operation. value1 = new ComplexNumber(); value1.real = 2; value1.imaginary = 3; value2 = new ComplexNumber(); value2.real = 4; value2.imaginary = 7; result = client.Multiply(value1, value2); Console.WriteLine("Multiply({0} + {1}i, {2} + {3}i) = {4} + {5}i", value1.real, value1.imaginary, value2.real, value2.imaginary, result.real, result.imaginary); if (result is ComplexNumberWithMagnitude) { Console.WriteLine("Magnitude: {0}", ((ComplexNumberWithMagnitude)result).Magnitude); } else { Console.WriteLine("No magnitude was sent from the service"); } // Call the Divide service operation. value1 = new ComplexNumber(); value1.real = 3; value1.imaginary = 7; value2 = new ComplexNumber(); value2.real = 5; value2.imaginary = -2; result = client.Divide(value1, value2); Console.WriteLine("Divide({0} + {1}i, {2} + {3}i) = {4} + {5}i", value1.real, value1.imaginary, value2.real, value2.imaginary, result.real, result.imaginary); if (result is ComplexNumberWithMagnitude) { Console.WriteLine("Magnitude: {0}", ((ComplexNumberWithMagnitude)result).Magnitude); } else { Console.WriteLine("No magnitude was sent from the service"); } //Closing the client gracefully closes the connection and cleans up resources client.Close(); Console.WriteLine(); Console.WriteLine("Press <ENTER> to terminate client."); Console.ReadLine(); } } }