WCF 学习总结6 -- WCF参数与返回值
2011-01-19 01:33
369 查看
Message是WCF信道层提供的一个类,在Message类里,数据被标识成一个XML Information Set, 简称为InfoSet。当数据从客户端传输给服务端时,binding里指定的消息编码协议将决定包含客户端所提供数据的Message对象将以何种形式提供给服务(服务端到客户端也一样)。然而,所有标准的binding都会使用将Message对象表示成XML InfoSet的编码协议。根据预定义binding的编码协议,XML InfoSet可能会使用各种标准的XML文本编码、MTOM或者二进制格式。也就是说从WCF应用层到传输将会是下面这个过程:
[序列化]>(XML InfoSet)>编码(Text/MTOM/Binary)>传输>解码>(XML InfoSet)>[反序列化]
WCF提供了两种XML序列化的工具来完成:DataContractSerializer和XmlSerializer。
那么,WCF支持哪些类型的参数和返回值呢?当然WCF里推荐使用[DataContract]和[DataMember]来定义复杂的自定义对象,但是在WebService时代里,我们知道只要实体类或者其父类被标识为[Serializable]就支持序列化了,就可以作为WebMethod的参数或者返回值了。在有些场景,已经生成好众多实体类,又不想一个个为每个属性加上[DataMember],自然很怀念[Serializable]。OK,下面来看看代码:
1. 服务契约:
2. 服务实现:
3. 客户端:
4. 运行:
运行结果说明:WCF除了支持默认的[DataContract]对象以外,还支持[Serializble]对象,DataTable也是被支持的。
但是用DataTable作为参数或者返回值时,DataTable的TableName一定不能为空。否则会抛出下面的错误:
The underlying connection was closed: The connection was closed unexpectedly.
光看这个异常,还真不知道哪不对了。。。就算你没有调用带DataTable的方法,也不行。很诡异的错误提示。。。
另外,大家注意到这个细节了没有。服务端定义的对象,在客户端生成的代理却是这样的?
所有字段都加上了 k__BackingField,原因是默认的DataContractSerializer序列化搞的鬼~
[DataContract]的Response Message XML InfoSet
[Serializable]的Response Message XML InfoSet,真是面目全非了。
原因应该是DataContractSerializer(System.Runtime.Serialization命名空间下)是在实际访问方法时,对参数列表的对象进行序列化。标识为[DataMember]特性的[DataContract]能够别识别并正常的序列化,而[Serializable]对象只能通过反射,而被反射出来的Public Field被加进去了不想要的东西。。。
解决办法也简单:就是改用XmlSerializer代替DataContractSerializer。只要在服务契约上声明一下:
[XmlSerializerFormat]特性就可以了。
[ServiceContract]
[XmlSerializerFormat]
public interface IService1
{
[OperationContract]
List<CustomerObjectOfDataContract> GetDataFromListOfDataContract();
[OperationContract]
List<CustomerObjectOfSerializable> GetDataFromListOfSerializable();
[OperationContract]
DataTable GetDataFromDataTable();
}
使用[XmlSerializerFormat]后,[Serializable]的Response Message XML InfoSet:
源码下载
本系列链接:
WCF 学习总结1 -- 简单实例
WCF 学习总结2 -- 配置WCF
WCF 学习总结3 -- 实例模式
WCF 学习总结4 -- 用Duplex实现消息广播
WCF 学习总结5 -- 消息拦截实现用户名验证
WCF 学习总结6 -- WCF参数与返回值
WCF 学习总结7 -- 流模式实现文件上传
WCF 学习总结8 –- WCF 事务处理
[序列化]>(XML InfoSet)>编码(Text/MTOM/Binary)>传输>解码>(XML InfoSet)>[反序列化]
WCF提供了两种XML序列化的工具来完成:DataContractSerializer和XmlSerializer。
那么,WCF支持哪些类型的参数和返回值呢?当然WCF里推荐使用[DataContract]和[DataMember]来定义复杂的自定义对象,但是在WebService时代里,我们知道只要实体类或者其父类被标识为[Serializable]就支持序列化了,就可以作为WebMethod的参数或者返回值了。在有些场景,已经生成好众多实体类,又不想一个个为每个属性加上[DataMember],自然很怀念[Serializable]。OK,下面来看看代码:
1. 服务契约:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.Data; namespace WcfService { [ServiceContract] //[XmlSerializerFormat] public interface IService1 { [OperationContract] List<CustomerObjectOfDataContract> GetDataFromListOfDataContract(); [OperationContract] List<CustomerObjectOfSerializable> GetDataFromListOfSerializable(); [OperationContract] DataTable GetDataFromDataTable(); } [DataContract] public class CustomerObjectOfDataContract { [DataMember] public string Name { get; set; } [DataMember] public string Code { get; set; } } [Serializable] public class CustomerObjectOfSerializable { public string Name { get; set; } public string Code { get; set; } } }
2. 服务实现:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; using System.Data; namespace WcfService { public class Service1 : IService1 { #region IService1 Members public List<CustomerObjectOfDataContract> GetDataFromListOfDataContract() { return new List<CustomerObjectOfDataContract> { new CustomerObjectOfDataContract { Name = "test1", Code = "aaa" }, new CustomerObjectOfDataContract { Name = "test2", Code = "bbb" }, new CustomerObjectOfDataContract { Name = "test3", Code = "ccc" } }; } public List<CustomerObjectOfSerializable> GetDataFromListOfSerializable() { return new List<CustomerObjectOfSerializable> { new CustomerObjectOfSerializable { Name = "test1", Code = "111" }, new CustomerObjectOfSerializable { Name = "test2", Code = "222" }, new CustomerObjectOfSerializable { Name = "test3", Code = "333" } }; } public DataTable GetDataFromDataTable() { var data = new DataTable("DataTable"); data.Columns.Add("Name", typeof(string)); data.Columns.Add("Code", typeof(string)); data.Rows.Add(new object[] { "aaa", "111" }); data.Rows.Add(new object[] { "bbb", "222" }); data.Rows.Add(new object[] { "ccc", "333" }); return data; } #endregion } }
3. 客户端:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; namespace WcfClient { class Program { static void Main(string[] args) { WcfSvc.Service1Client client = new WcfSvc.Service1Client(); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("List<T> DataContract:"); var data1 = client.GetDataFromListOfDataContract(); foreach (var obj in data1) Console.WriteLine("{0},{1}", obj.Name, obj.Code); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("List<T> Serializable:"); var data2 = client.GetDataFromListOfSerializable(); foreach (var obj in data2) Console.WriteLine("{0},{1}", obj.Namek__BackingField, obj.Codek__BackingField); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("System.Data.DataTable:"); var data3 = client.GetDataFromDataTable(); foreach (DataRow row in data3.Rows) Console.WriteLine("{0},{1}", row[0], row[1]); Console.WriteLine(); Console.Read(); } } }
4. 运行:
运行结果说明:WCF除了支持默认的[DataContract]对象以外,还支持[Serializble]对象,DataTable也是被支持的。
但是用DataTable作为参数或者返回值时,DataTable的TableName一定不能为空。否则会抛出下面的错误:
The underlying connection was closed: The connection was closed unexpectedly.
光看这个异常,还真不知道哪不对了。。。就算你没有调用带DataTable的方法,也不行。很诡异的错误提示。。。
另外,大家注意到这个细节了没有。服务端定义的对象,在客户端生成的代理却是这样的?
所有字段都加上了 k__BackingField,原因是默认的DataContractSerializer序列化搞的鬼~
[DataContract]的Response Message XML InfoSet
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Header /> <s:Body> <GetDataFromListOfDataContractResponse xmlns="http://tempuri.org/"> <GetDataFromListOfDataContractResult xmlns:a="http://schemas.datacontract.org/2004/07/WcfService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <a:CustomerObjectOfDataContract> <a:Code>aaa</a:Code> <a:Name>test1</a:Name> </a:CustomerObjectOfDataContract> <a:CustomerObjectOfDataContract> <a:Code>bbb</a:Code> <a:Name>test2</a:Name> </a:CustomerObjectOfDataContract> <a:CustomerObjectOfDataContract> <a:Code>ccc</a:Code> <a:Name>test3</a:Name> </a:CustomerObjectOfDataContract> </GetDataFromListOfDataContractResult> </GetDataFromListOfDataContractResponse> </s:Body>
[Serializable]的Response Message XML InfoSet,真是面目全非了。
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Header /> <s:Body> <GetDataFromListOfSerializableResponse xmlns="http://tempuri.org/"> <GetDataFromListOfSerializableResult xmlns:a="http://schemas.datacontract.org/2004/07/WcfService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <a:CustomerObjectOfSerializable> <a:_x003C_Code_x003E_k__BackingField>111</a:_x003C_Code_x003E_k__BackingField> <a:_x003C_Name_x003E_k__BackingField>test1</a:_x003C_Name_x003E_k__BackingField> </a:CustomerObjectOfSerializable> <a:CustomerObjectOfSerializable> <a:_x003C_Code_x003E_k__BackingField>222</a:_x003C_Code_x003E_k__BackingField> <a:_x003C_Name_x003E_k__BackingField>test2</a:_x003C_Name_x003E_k__BackingField> </a:CustomerObjectOfSerializable> <a:CustomerObjectOfSerializable> <a:_x003C_Code_x003E_k__BackingField>333</a:_x003C_Code_x003E_k__BackingField> <a:_x003C_Name_x003E_k__BackingField>test3</a:_x003C_Name_x003E_k__BackingField> </a:CustomerObjectOfSerializable> </GetDataFromListOfSerializableResult> </GetDataFromListOfSerializableResponse> </s:Body> </s:Envelope>
原因应该是DataContractSerializer(System.Runtime.Serialization命名空间下)是在实际访问方法时,对参数列表的对象进行序列化。标识为[DataMember]特性的[DataContract]能够别识别并正常的序列化,而[Serializable]对象只能通过反射,而被反射出来的Public Field被加进去了不想要的东西。。。
解决办法也简单:就是改用XmlSerializer代替DataContractSerializer。只要在服务契约上声明一下:
[XmlSerializerFormat]特性就可以了。
[ServiceContract]
[XmlSerializerFormat]
public interface IService1
{
[OperationContract]
List<CustomerObjectOfDataContract> GetDataFromListOfDataContract();
[OperationContract]
List<CustomerObjectOfSerializable> GetDataFromListOfSerializable();
[OperationContract]
DataTable GetDataFromDataTable();
}
使用[XmlSerializerFormat]后,[Serializable]的Response Message XML InfoSet:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Header /> <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <GetDataFromListOfSerializableResponse xmlns="http://tempuri.org/"> <GetDataFromListOfSerializableResult> <CustomerObjectOfSerializable> <Name>test1</Name> <Code>111</Code> </CustomerObjectOfSerializable> <CustomerObjectOfSerializable> <Name>test2</Name> <Code>222</Code> </CustomerObjectOfSerializable> <CustomerObjectOfSerializable> <Name>test3</Name> <Code>333</Code> </CustomerObjectOfSerializable> </GetDataFromListOfSerializableResult> </GetDataFromListOfSerializableResponse> </s:Body> </s:Envelope>
源码下载
本系列链接:
WCF 学习总结1 -- 简单实例
WCF 学习总结2 -- 配置WCF
WCF 学习总结3 -- 实例模式
WCF 学习总结4 -- 用Duplex实现消息广播
WCF 学习总结5 -- 消息拦截实现用户名验证
WCF 学习总结6 -- WCF参数与返回值
WCF 学习总结7 -- 流模式实现文件上传
WCF 学习总结8 –- WCF 事务处理
相关文章推荐
- 2016年12月9日学习总结---- 函数参数和返回值
- WCF 学习总结3 -- 实例模式
- WCF学习笔记三:使用自定义行为扩展WCF总结
- JNI学习二:字符串参数传递与返回值
- Asp Conn.execute的参数与返回值总结
- 用VisualStudio2010学习WCF服务编程总结(1)操作重载
- WCF 学习总结2 -- 配置WCF
- C/C++ 日常学习总结(第十八篇)参数个数可变的函数
- (原创)c#学习笔记06--函数02--变量的作用域02--参数和返回值与全局数据
- 对于最近学习WCF的总结——唔聊的服务
- Solr学习总结Solr查询参数
- WCF学习总结——WCF消息交换模式
- Maven学习总结(42)——Maven多模块构建中常用的参数
- Spring3 MVC (三)---请求处理方法 参数及返回值总结
- ES6学习总结之函数变化(函数默认参数,扩展运算符,箭头函数)
- 对PHP中GD库的一些画图函数、及函数参数的学习总结(一)
- 深度学习调试参数总结
- Java基础学习笔记十二 类、抽象类、接口作为方法参数和返回值以及常用API
- WCF 学习总结2 -- 配置WCF
- 花行九颜~vs、Linux参数配置对照学习知识点总结