程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> C# 操作 XML 數據庫類型、Oracle XMLType

C# 操作 XML 數據庫類型、Oracle XMLType

編輯:關於C#

xml 類型很久就有了一直沒有關注,有時間正好看看;

這次學習要做的事情

1、設計一個C# 類來生成 XML Schema (XML 架構)

先設計類,然後由類生成表可能是有很多人蒙昧以求的;正好和 ORM 相反

最主要的 XML 架構可以用來約束數據庫中的;XML 的有效性

2、基於上一步的XML Schema我們創建一個數據表、並包含這個和上邊 C# 類對應的的 XML 類型;

3、插入一些數據;

4、改變 C# 的類重新生成 Schema在更新數據庫中的 Schema

這步假設業務變更看看,更改如何進行,是否方便等

5、用 C# 寫一個網頁,顯示這些數據;

6、階段性總結

好開始:

一、設計一個C# 類來生成 XML Schema類代碼:

類如下

namespace Model
{

    /// <summary>
    /// 電話
    /// </summary>
    public class Phone
    {
        string _code;
        /// <summary>
        /// 電話號
        /// </summary>
        public string Code
        {
            get { return _code; }
            set { _code = value; }
        }
        PhoneType _type;
        public PhoneType Type
        {
            get { return _type; }
            set { _type = value; }
        }
    }
    /// <summary>
    /// 電話類型
    /// </summary>
    public enum PhoneType
    {
        [XmlEnum(Name = "未知")] //XML 序列化用的名稱
        Unknown,
        [XmlEnum(Name = "移動")]
        Mobile,
        [XmlEnum(Name = "固定")]
        Fixed,
    }
    /// <summary>
    /// 電話集合
    /// </summary>
    [XmlRoot("Phones")]
    public class Phones : List<Phone>
    {
        public void Add(string code, PhoneType type)
        {
            base.Add(new Phone() { Code = code, Type = type });
        }
    }
}

3個類:電話、電話類型(枚舉)、電話類型集合;

以Phones 類生成 Schema ,用 .net sdk 的 xsd.exe 或自己寫代碼都可以生成的、我就不多說了;

Schema 這東西如果純手寫也是勞動量巨大的;謝謝.net 為我們提供這個功能吧;

Schema如下:

<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Phones" nillable="true" type="ArrayOfPhone" />
  <xs:complexType name="ArrayOfPhone">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="unbounded" name="Phone" nillable="true" type="Phone" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="Phone">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="Code" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="Type" type="PhoneType" />
    </xs:sequence>
  </xs:complexType>
  <xs:simpleType name="PhoneType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="未知" />
      <xs:enumeration value="移動" />
      <xs:enumeration value="固定" />
    </xs:restriction>
  </xs:simpleType>
</xs:schema>

二、我們創建一個數據表、並包含這個和上邊 C# 類對應的的 XML 類型;

BEGIN
    -- 注冊架構
  DBMS_XMLSCHEMA.registerschema(schemaurl => 'http://www.OracleDemo.com/Phones.xsd',
                                schemadoc => '<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Phones" nillable="true" type="ArrayOfPhone" />
  <xs:complexType name="ArrayOfPhone">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="unbounded" name="Phone" nillable="true" type="Phone" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="Phone">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="Code" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="Type" type="PhoneType" />
    </xs:sequence>
  </xs:complexType>
  <xs:simpleType name="PhoneType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="未知" />
      <xs:enumeration value="移動" />
      <xs:enumeration value="固定" />
    </xs:restriction>
  </xs:simpleType>
</xs:schema>',

                                local     => TRUE,
                                gentypes  => TRUE,
                                genbean   => FALSE,
                                gentables => TRUE);
END;
--會建立 xml 描述的【Oracle自定義類型】
-- 如果用Oracle 工具查看 Types 下會出現一些如 Phone***_T,phone***_coll,ArrayOfPhone***_T 類似名稱的 【Oracle自定義類型】
-- 結構就和xml schema 是一樣
-- gentables => TRUE 還會建立一些表;
-- 如 create table Phones721_TAB of SYS.XMLTYPE --物理表
-- create table SYS_NTyIVemDaJQXqHZgjqYv+haQ== of Phone711_T --自定義類型表

2.2 創建表

CREATE TABLE XML_USER_INFO (
 NPK integer,
 USER_NAME NVARCHAR2(50),
 Phones XMLType,
 primary key (NPK)
)
XMLTYPE COLUMN Phones STORE AS OBJECT RELATIONAL          --以對象關系方式建立,而不是二進制
XMLSCHEMA "http://www.OracleDemo.com/Phones.xsd"
ELEMENT "Phones"
VARRAY Phones.XMLDATA."Phone" STORE AS TABLE XML_USER_INFO_XMLNT01 --將xml中 Phones/Phone 定義為一個數組嵌套表
/
-- 返回:成功
-- 這時 oracle 還會建立一個 XML_USER_INFO_XMLNT01 的【嵌套表】

我建立了一個叫 XML_USER_INFO 的表,這個東西假設為一個用戶信息表(真正的用戶信息不可能這麼少列的)

oracle 建立XMLType 時可以指定以二進制或對象關系方式建立xml 類型,

我這裡選擇STORE AS OBJECT RELATIONAL 據說可以提高性能,等待考證

下一面我要給 Phones/Phone 下加一些約束所以將 Phone 定義為一個數組嵌套表;說白了就是定義一個表裡面都放置 Phone 類型

2.3 定義約束

ALTER TABLE XML_USER_INFO_XMLNT01
ADD constraint PK_XML_USER_INFO_XMLNT01 primary key (NESTED_TABLE_ID, "Code")

就是每個人的電話號碼不能重復; 數據庫裡的東西要是沒有約束是很郁悶的,這裡也試驗一下這個問題;

constraint PK_XML_USER_INFO_XMLNT01 可以省略,因為有名字比較容易從異常中看出到底是什麼列出錯,我一般都會加上這個

除非一個表就一個主鍵;

遺憾的是集合類型的元素、不能加外鍵(就是說如果是 Phones 的屬性是可以加外鍵的 Phone屬性,如電話號碼,類型什麼的就不行,必須和數據庫表一行能對應上的才可以加外鍵否則只能用 schema 約束);

外鍵的例子以後在說吧;

2.4 本步驟總結

比較滿意、雖然sql 代碼很多不過,schema 是類生成的、能夠節省一些設計時間,不過學習成本是必須的;

XMLType 的 schema 並不是必須的,不過沒有約束,關系對數據庫來說,時間長了是不好維護的尤其是後來人;

三、插入一些數據

3.1 插入數據

INSERT INTO XML_USER_INFO VALUES (1,'用戶1'
    ,XMLType('<Phones xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http:www.w3.org/2001/XMLSchema">
 <Phone>
    <Code>13940588000</Code>
    <Type>移動</Type>
 </Phone>
 <Phone>
    <Code>024-22222222-1</Code>
    <Type>固定</Type>
 </Phone>
 <Phone>
    <Code>8788888</Code>
    <Type>未知</Type>
 </Phone>
</Phones>'
    )
)
/

3.2測試約束有效性(在Type改小靈通)

INSERT INTO XML_USER_INFO VALUES (2,'用戶2'
    ,XMLType('<Phones xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http:www.w3.org/2001/XMLSchema">
  <Phone>
    <Code>13940588000</Code>
    <Type>移動</Type>
  </Phone>
  <Phone>
    <Code>13940588000</Code>
    <Type>固定</Type>
  </Phone>
  <Phone>
    <Code>8788888</Code>
    <Type>小靈通</Type>
  </Phone>
</Phones>'
    )
)
-- 返回錯誤:ORA-31038: enumeration 值無效: "小靈通"
-- 證明 PhoneType 的 XMLSchema 約束是有效的(廢話);
/

….插入我就不多寫了都這模樣

注意:大家可以看出xmltype這裡的數據,就是 Phones 對象xml序列化後的的樣子

也就是說我們可以比較方便把類直接插入數據庫,如果用平面表這裡要執行 1*n次的 insert

代碼例子我就不提供了先,因為太簡單了 XMLType( :XMLString ) 然後給對象序列化了

給到 :XMLString 參數裡就可以了;

3.3 本步驟總結

如果開發的話,可以節省一些語句;尤其適合那種,子表數據一次插入很少的情況

而且用 select 讀取的時候,應該可以直接反序列化為對象

能省去往實體類賦值的代碼量;

四、改變 C# 的類重新生成 Schema在更新數據庫中的 Schema

假設我們的業務更改:那個業務也不可能不變的對吧,所以測試是否容易修改也是必要的

假設,【電話】加入了說明屬性、【電話類型】加入了小靈通;集合類沒更改;

4.1 類的更改

public class Phone
{
    string _code;
    public string Code
    {
        get { return _code; }
        set { _code = value; }
    }
    PhoneType _type;
    public PhoneType Type
    {
        get { return _type; }
        set { _type = value; }
    }
    //新加入
    string _make;
    public string Make
    {
        get { return _make; }
        set { _make = value; }
    }
}
public enum PhoneType
{
    [XmlEnum(Name = "未知")]
    Unknown,
    [XmlEnum(Name = "移動")]
    Mobile,
    [XmlEnum(Name = "固定")]
    Fixed,
    [XmlEnum(Name = "小靈通")] //新加入
    PHS
}

4.2 重新生成 xsd

方法在上面說過了

4.3 數據庫更改

declare
   -- 舊Schema
  oldSchemaDoc nvarchar2(2000) := '<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Phones" nillable="true" type="ArrayOfPhone" />
  <xs:complexType name="ArrayOfPhone">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="unbounded" name="Phone" nillable="true" type="Phone" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="Phone">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="Code" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="Type" type="PhoneType" />
    </xs:sequence>
  </xs:complexType>
  <xs:simpleType name="PhoneType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="未知" />
      <xs:enumeration value="移動" />
      <xs:enumeration value="固定" />
    </xs:restriction>
  </xs:simpleType>
</xs:schema>';
 -- 新Schema
  newSchemaDoc nvarchar2(2000) := '<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Phones" nillable="true" type="ArrayOfPhone" />
  <xs:complexType name="ArrayOfPhone">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="unbounded" name="Phone" nillable="true" type="Phone" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="Phone">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="Code" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="Type" type="PhoneType" />
      <xs:element minOccurs="0" maxOccurs="1" name="Make" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
  <xs:simpleType name="PhoneType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="未知" />
      <xs:enumeration value="移動" />
      <xs:enumeration value="固定" />
      <xs:enumeration value="小靈通" />
    </xs:restriction>
  </xs:simpleType>
</xs:schema>';
 -- 變量存儲 xmldiff xml差異結果
 diffXMLDoc clob;
 -- url
 v_schema_url nvarchar2(255) :=  'http://www.OracleDemo.com/Phones.xsd';
begin
   --生成差異結果
  select xmldiff(xmltype(oldSchemaDoc),xmltype(newSchemaDoc)).getClobVal() into diffXMLDoc from dual;
  -- 11g 新增加的原地更改函數,性能比原來那個 DBMS_XMLSCHEMA.copyEvolve
  -- 概念 執行原地 XML 模式演變 http://www.oracle.com/technology/global/cn/obe/11gr1_db/datamgmt/xmldb2_a/xmldb2_a.htm
  DBMS_XMLSCHEMA.inPlaceEvolve( v_schema_url ,  xmltype(diffXMLDoc));
end;

流程就是拿 舊的Schema 和 新的Schema 比較生成一個差異結果 select xmldiff...這裡,然後調用 DBMS_XMLSCHEMA.inPlaceEvolve

更新 Schema很簡單函數就用到兩個,代碼沒多少就是2個Schema 占地方;

4.4 在插入一條數據看看

--- 在插入一條
INSERT INTO XML_USER_INFO VALUES (3,'用戶3'
    ,XMLType('<Phones xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Phone>
    <Code>13930588003</Code>
    <Type>移動</Type>
  </Phone>
  <Phone>
    <Code>024-32222223-1</Code>
    <Type>固定</Type>
    <Make>公司</Make>
  </Phone>
  <Phone>
    <Code>024-3788883</Code>
    <Type>小靈通</Type>
    <Make>公司</Make>
  </Phone>
</Phones>'
    )
)
/

4.5 本步驟總結

更改還是比較方便的;數據庫更改在所難免可以快速的更改才是王道;不過例子比較片面,大待大規模測試

五、用 C# 寫一個網頁,顯示這些數據;

5.1 顯示效果

顯示效果如下(砢碜點啊)

 

NPK

用戶名

電話信息

1

用戶1

【13940588000;移動;】、【024-22222222-1;固定;】、【12345678;未知;】

2

用戶2

【13920588002;移動;】、【024-22222222-1;固定;】、【2788882;未知;】

3

用戶3

【024-32222223-1;固定;公司】、【024-3788883;小靈通;公司】、【13930588003;移動;】

4

用戶4

【13940588004;移動;個人】、【024-42222224-1;固定;公司】、【024-4788884;小靈通;公司】、【024-4788844;固定;個人】

5

用戶5

【13950588005;移動;個人】、【024-52222225-1;固定;公司】、【024-5788885;小靈通;公司】、【024-5788845;固定;個人】

 

5.2 代碼

//用戶信息(XML_USER_INFO 是一樣的)

public class UserInfo
    {
        int _NPK;
        public int NPK
        {
            get { return _NPK; }
            set { _NPK = value; }
        }
        string _userName;
        public string UserName
        {
            get { return _userName; }
            set { _userName = value; }
        }
        Phones _phones;
        public Phones Phones
        {
            get { return _phones; }
            set { _phones = value; }
        }
    }

//數據操作代碼 (很簡單就是,把數據全差出來)

public class UserInfoDAL
{
    public static List<UserInfo> GetUserInfos()
    {
        //我本身是想用 ODP.net 不過為了讓大家看懂就先用 ms 的 OracleClient
        //其實  ODP.net 操作 oracle 更方便點;直接支持 xmlType 的不用轉換
        //GETSTRINGVAL 是 Oracle xmlType 類的一個函數,以string 形式返回xml 對象數據
        const string SQL = "SELECT t.NPK,t.USER_NAME, t.PHONES.GETSTRINGVAL() as  PHONES FROM XML_USER_INFO t order by 1";
        List<UserInfo> models = new List<UserInfo>();
        string connstring = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
        using (OracleConnection conn = new OracleConnection(connstring))
        {
            conn.Open();
            using (OracleCommand cmd = new OracleCommand(SQL, conn))
            {
                using (OracleDataReader reader = cmd.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        DataTable dt = reader.GetSchemaTable();
                        int npk = Convert.ToInt32(reader["NPK"]);
                        string userName = reader["USER_NAME"].ToString();
                        //讀取 XML 類型
                        string phonesXml = reader["PHONES"].ToString();
                        //這裡是我自己擴展的全局的 string 對象的方法,就是把 string 通過XML反序列化為對象實例
                        Phones ps = phonesXml.XMLToObject<Phones>();
                        models.Add(new UserInfo() { NPK = npk, UserName=userName, Phones = ps });
                    }
                }
            }
        }
        return models;
    }
}

//網頁的代碼

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="ObjectDataSource1">
    <Columns>
        <asp:BoundField DataField="NPK" HeaderText="NPK" SortExpression="NPK" />
        <asp:BoundField DataField="UserName" HeaderText="用戶名" SortExpression="UserName" />
        <asp:BoundField DataField="Phones" HeaderText="電話信息" SortExpression="Phones" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
    SelectMethod="GetUserInfos" TypeName="WebApplication1.UserInfoDAL">
</asp:ObjectDataSource>

--可能有人會問;你的網頁【電話信息】那列怎麼顯示成那個樣子的?

--其實很簡單;重寫 Phones 類的 ToString() 想怎麼顯示都可以的,我這裡就不貼代碼了

--如果有人需要說一聲,因為不屬於本片博文的技術討論范圍就不貼上了;

5.3 本步驟總結

讀取操作也是很方便的,比如在做分頁的時候,就比連接2個表(用戶表、電話表)要方便的多

而且返回數據,可以直接轉換為對象更加方便了;不過數據量大的子表就不太適合了這種方式

6、總結

目前看來

XMLType比起平面表更加類似 class 類

因為.net 可以直接通過類生成Schema在生成 XMLType 數據庫表,能節省一些數據表的設計時間,改完對象直接更改數據庫就可以了

遺憾:C# 視乎生成不了Schema的很多約束屬性如限制字段長度的約束

還有集合內的類型,加不了外鍵比如本來我要是把 電話類型放到一個平面表裡

就加不上外鍵了;

處理那些樹形的數據比較適合;

如一個主記錄、關聯n個子記錄的情況

查詢插入都是比較方便的,直接可以序列化或反序列化為對象或xml 能節省些代碼;

遺憾:XML 序列化可能慢點,不過可以使操作更明晰,如果直接操作 xml 的話太頭痛了代碼也亂

終於寫完了,貼了這麼多代碼,園子裡用Oracle 的不多,可能有人看不懂,不過問我就好了

我可以解答你,大家共同學習進步吧!

不過本人公司基本都是玩 oracle 沒有時間去玩 sqlserver 抱歉了先

如果有時間在做個 sqlserver 的例子吧!

或者出一個 sqlserver 和 oracle 的 xml 類型對比性測試,如果有人願意和我做這次試驗,我可以提供 oracle 的測試機和代碼

我對sqlserver 不是很熟悉的,已經n年沒用了,如果我自己出對比測試,怕糟蹋了 sqlserver

我家的計算機是 AMD6000 +、4G內存

這種測試應該是沒啥問題的,跑些不是超級大的應用還是可以的;

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved