• 关于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>
  • 相关阅读:
    《CLR via C#》读书笔记 之 基元类型、引用类型和值类型 明
    《CLR via C#》读书笔记 之 类型和成员基础 明
    《CLR via C#》读书笔记 之 方法 明
    设计模式基础(一):UML中关系图解 明
    【转载】c#类的成员初始化顺序 明
    《CLR via C#》读书笔记 之 接口 明
    《CLR via C#》读书笔记 之 事件 明
    《CLR via C#》读书笔记 之 参数 明
    三塔DP——http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3554
    二分求幂——http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3549
  • 原文地址:https://www.cnblogs.com/rbzz/p/14359484.html
Copyright © 2020-2023  润新知