關于HTTP調用WCF傳遞DataTable參數的處理
在上兩節中,已經可以跨域調用WCf提供的服務了,但是如果參數是DataTable的話,就有點麻煩了
但是好在傳遞DataTable的xml文檔其實是固定的,你可以字符串拼接(如果是前端JS的話,可能是免不了的)
現在來分析一下傳遞的DataTable的xml結構
下面是我生成的DataTable
private DataTable InitTable() { DataTable dt = new DataTable("table"); dt.Columns.Add("ID"); dt.Columns.Add("IName"); for (int i = 0; i < 5; i++) { dt.Rows.Add(i, "a" + i); } dt.AcceptChanges(); dt.Rows[0][1] = "update"; dt.Rows[1].Delete(); dt.Rows[3].Delete(); dt.Rows.Add(100, "Add"); dt.Columns.Add("type", typeof(UserInfoModel)); foreach (DataRow row in dt.Rows) { if (row.RowState != DataRowState.Deleted) { row["type"] = new UserInfoModel() { NickName = row["IName"].ToString() }; } } return dt; }

可以看到我現在的DataTable的結構就是6行3列,其中第2行和第4行是被刪除了的,而第1行是經過更新了的,第6行是新添加的
下面是對照上面的DataTable生成的帶注釋的xml文檔
<?xml version="1.0"?> <!--這個節點是固定的,表示這是一個DataTable類型,并且命名空間也是必須要有的--> <DataTable xmlns="http://schemas.datacontract.org/2004/07/System.Data"> <!--這個節點是固定的,表示DataTable的結構--> <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <!--這個節點也是固定的,但 msdata:MainDataTable 的值不是固定,該屬性的取值為你DataTable的TableName 此值配合下面的 diffgr:diffgram -> DocumentElement -> table(此標簽就是TableName)--> <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="table" msdata:UseCurrentLocale="true"> <xs:complexType> <!--這個是固定的--> <xs:choice minOccurs="0" maxOccurs="unbounded"> <!--這里的name就是DataTable的TableName--> <xs:element name="table"> <xs:complexType> <xs:sequence> <!--下面三個就代表這個Table一共有三列,且分別定義了它們的數據類型信息 這里要注意的是,如果是數據類型是基元類型,則其type的值就是 xs:string--> <xs:element name="ID" type="xs:string" minOccurs="0" /> <xs:element name="IName" type="xs:string" minOccurs="0" /> <!--而如果非基元類型,則是它的assembly qualified type name 且還需要在WCF的配置文件中指定, 參考msdn文檔 https://docs.microsoft.com/zh-cn/dotnet/framework/data/adonet/dataset-datatable-dataview/security-guidance--> <xs:element name="type" msdata:DataType="PublicModel.UserInfoModel, PublicModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" type="xs:anyType" minOccurs="0" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> </xs:element> </xs:schema> <!--這個節點固定的--> <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"> <!--這個節點也是固定的,但要注意一定要有 xmlns="" 空命名空間屬性,不可或缺--> <DocumentElement xmlns=""> <!--以下幾個table節點就是動態的,表示行信息,其中節點table就是該DataTable的TableName id值也是固定的以TableName+當前行的下標+1來命名 msdata:rowOrder則是其真正的下標 diffgr:hasChange 表示此行的更新信息,只有 modified 和 inserted 兩種取值,如果行未做任何更改,則不用寫入此特性--> <table diffgr:id="table1" msdata:rowOrder="0" diffgr:hasChanges="modified"> <ID>0</ID> <IName>update</IName> <!--這個列的名稱就叫type,命名空間是固定的,必須要這么寫上去--> <type xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Rid>0</Rid> <RLevel>0</RLevel> <NickName>update</NickName> <!--這個屬性的值表示它是一個可以為null的值--> <BirthDay xsi:nil="true" /> </type> </table> <table diffgr:id="table3" msdata:rowOrder="2" diffgr:hasChanges="modified"> <ID>2</ID> <IName>a2</IName> <type xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Rid>0</Rid> <RLevel>0</RLevel> <NickName>a2</NickName> <BirthDay xsi:nil="true" /> </type> </table> <table diffgr:id="table5" msdata:rowOrder="4" diffgr:hasChanges="modified"> <ID>4</ID> <IName>a4</IName> <type xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Rid>0</Rid> <RLevel>0</RLevel> <NickName>a4</NickName> <BirthDay xsi:nil="true" /> </type> </table> <table diffgr:id="table6" msdata:rowOrder="5" diffgr:hasChanges="inserted"> <ID>100</ID> <IName>Add</IName> <type xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Rid>0</Rid> <RLevel>0</RLevel> <NickName>Add</NickName> <BirthDay xsi:nil="true" /> </type> </table> </DocumentElement> <!--這里是保存了行更新之前的所有行信息--> <diffgr:before> <!--這個就是表示在之前的下標為0處的行,在更新/刪除之前它的值信息, 其中 xmlns="" 是必須要有的,否則更新的行和刪除的行信息將無法傳遞到WCF 而其命名規則與上面是一致的--> <table diffgr:id="table1" msdata:rowOrder="0" xmlns=""> <ID>0</ID> <IName>a0</IName> </table> <table diffgr:id="table2" msdata:rowOrder="1" xmlns=""> <ID>1</ID> <IName>a1</IName> </table> <table diffgr:id="table3" msdata:rowOrder="2" xmlns=""> <ID>2</ID> <IName>a2</IName> </table> <table diffgr:id="table4" msdata:rowOrder="3" xmlns=""> <ID>3</ID> <IName>a3</IName> </table> <table diffgr:id="table5" msdata:rowOrder="4" xmlns=""> <ID>4</ID> <IName>a4</IName> </table> </diffgr:before> </diffgr:diffgram> </DataTable>
下面是C#的序列化代碼
public string SerializaTableToXml(DataTable dt) { //檢查Table的名稱是否為空 if (string.IsNullOrWhiteSpace(dt.TableName)) { //如果為空,則給一個命名,一定要有名稱 dt.TableName = "table"; } //檢查Table的命名空間是否為空 if (!string.IsNullOrWhiteSpace(dt.Namespace)) { //如果不為空,則一定要刪除它的命名空間 dt.Namespace = null; } var stream = new MemoryStream(); XmlSerializer xs = new XmlSerializer(dt.GetType()); XmlWriterSettings settings = new XmlWriterSettings() { CheckCharacters = true, CloseOutput = true, ConformanceLevel = ConformanceLevel.Auto, Encoding = new UTF8Encoding(false), DoNotEscapeUriAttributes = true, NamespaceHandling = NamespaceHandling.OmitDuplicates, NewLineHandling = NewLineHandling.Entitize, NewLineOnAttributes = false, OmitXmlDeclaration = false, WriteEndDocumentOnClose = true }; using (var writer = XmlWriter.Create(stream, settings)) { xs.Serialize(stream, dt); } string xmlStr = Encoding.UTF8.GetString(stream.ToArray()); stream.Dispose(); //下面還需要做一些其它的事情,所以裝載到xmldocument中 var xml = new XmlDocument(); xml.LoadXml(xmlStr); //給根路徑添加命名空間,這是必須的 xml.DocumentElement.SetAttribute("xmlns", "http://schemas.datacontract.org/2004/07/System.Data"); //創建一個空的命名空間,這也是必須的 var attr = xml.CreateAttribute("xmlns"); attr.Value = ""; //找到第一個子節點下的DocumentElement子節點(因為DataTable序列化后是固定的,所以直接下標獲取) var element = xml.DocumentElement.ChildNodes[1].ChildNodes[0]; //設置命名空間 element.Attributes.SetNamedItem(attr); //找到所有的diffgr:before節點下的子節點 var elements = xml.DocumentElement.ChildNodes[1].ChildNodes[1].ChildNodes; foreach (XmlNode node in elements) { //循環添加空命名空間,這是必須的 node.Attributes.SetNamedItem(attr); } return xml.OuterXml; }
如果DataTable中的數據類型都是基元類型,那么這么做就完事了,但我提供的示例里其中type列是一個自定義類型
這種情況下,WCF的配置文件中需要加入以下信息
<!--加入自定義類型信息,給予接收DataTable類型使用,configSections必須是 configuration 節點下的第一個--> <configSections> <!--sectionGroup 節點直接這樣照寫就行了--> <sectionGroup name="system.data.dataset.serialization" type="System.Data.SerializationSettingsSectionGroup, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <section name="allowedTypes" type="System.Data.AllowedTypesSectionHandler, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> </sectionGroup> </configSections> <!--這里就是存放自定義類型信息的地方--> <system.data.dataset.serialization> <!--允許的自定義類型--> <allowedTypes> <!--添加一個自定義類型,它的type必須是程序集的完全名稱--> <add type="PublicModel.UserInfoModel, PublicModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </allowedTypes> </system.data.dataset.serialization>

浙公網安備 33010602011771號