C# 操作 XML 数据库类型、Oracle XMLType

  最近练习一下 Oracle11g XML 类型的数据、看看这东西到底怎么样

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 类型;

2.1 注册架构:

 

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内存 这种测试应该是没啥问题的,跑些不是超级大的应用还是可以的;

 

很晚了,错字明天在挑吧 呵呵; 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值