代码改变世界

读取xml通过deserialize和linq to sql快速更新数据表

2008-04-25 10:43  BAsil  阅读(2846)  评论(10编辑  收藏  举报

本文目的

本文旨在提供通过读取xml文件快速更新数据表的方法,为本人在项目应用中的实践所的,所涉及到的内容都比较基础,还请各位博友拍砖。

序幕

Linq to sql给我们的orm影射带来了极大的便利,实体类写很少的代码就可以完成数据库表的增删查改,也使我们能够更加专注于业务逻辑;而Xml的应用也是相当广泛,如今的SOA很大程度上是利用了Xml格式的SOAP消息来进行交互。有的时候我们会碰到需要读取xml保存到数据库的情况,在没有Linq的时候,可能我们会通过XmlSerializer的Deserialize方法来反序列化Xml生成相对应的net class,然后操作net class插入数据库,如果不采用orm的话,代码量和利用XmlDocument直接操作Xml更新数据库没有什么差别,那么现在有了Linq to sql,反序列化的net class和Linq to sql的Entity可以优雅的结合到一起,看看减轻了我们多少的工作量?

XmlSerializer.Deserialize 

首先我们看一下XmlSerializer.Deserialize的例子
使用的xml文件
norm.xml

<Norm>
  
<TradeName>纺织</TradeName>
  
<SubmittedDate>2008-04-28</SubmittedDate>
  
<Index>
    
<IndexName>信息化投入比</IndexName>
    
<IndexNum>500</IndexNum>
  
</Index>
  
<Index>
    
<IndexName>环保改善率</IndexName>
    
<IndexNum>300</IndexNum>
  
</Index>
</Norm>

对应的net class
norm.cs

public class Norm
    
{
        
private int normID;
        
private string tradeName;
        
private DateTime submittedDate;
        
private Index[] indexs;
        [XmlIgnore]
        
public int NormID
        
{
            
get{return normID;}
            
set{normID=value;}
        }

        [XmlElement(
"TradeName")]
        
public string TradeName
        
{
            
get{return tradeName;}
            
set{tradeName=value;}
        }

        [XmlElement(
"SubmittedDate")]
        
public DateTime SubmittedDate
        
{
            
get{return submittedDate;}
            
set{submittedDate=value;}
        }

        [XmlElement(
"Index")]
        
public Index[] Indexs
        
{
            
get{return indexs;}
            
set{indexs=value;}
        }

        
    }

    
public class Index
    
{
        
private int indexID;
        
private string indexName;
        
private float indexNum;
        
private int normID;
        [XmlIgnore]
        
public int IndexID
        
{
            
get{return indexID;}
            
set{indexID=value;}
        }

        [XmlElement(
"IndexName")]
        
public string IndexName
        
{
            
get{return indexName;}
            
set{indexName=value;}
        }

        [XmlElement(
"IndexNum")]
        
public float IndexNum
        
{
            
get{return indexNum;}
            
set{indexNum=value;}
        }

        [XmlIgnore]
        
public int NormID
        
{
            
get{return normID;}
            
set{normID=value;}
        }

    }

注意Index[] Indexs的Attribute设置为XmlElement("Index"),CLR可以帮助我们将多个Index Element反序列化为Indexs数组。
 Program.cs

class Program
    
{
        
static void Main(string[] args)
        
{
            XmlDocument responseXmlDocument 
= new XmlDocument();
            responseXmlDocument.Load(
"C:\\a.xml");

            XmlNode xn 
= responseXmlDocument.DocumentElement.ParentNode;

            XmlSerializer serializer 
= new XmlSerializer(typeof(Norm));
            Norm norm 
= serializer.Deserialize(new XmlNodeReader(xn)) as Norm;
            Console.WriteLine(
"Succeed!");
        }

    }



反序列化结合Linq to sql使用

首先给出表结构

CREATE TABLE [dbo].[Norm] (
    
[NormID] [int] IDENTITY (11NOT NULL ,
    
[TradeName] [nvarchar] (30NOT NULL ,
    
[SubmittedDate] [datetime] NOT NULL 
ON [PRIMARY]
CREATE TABLE [dbo].[Index] (
    
[IndexID] [int] IDENTITY (11NOT NULL ,
    
[IndexName] [nvarchar] (30NOT NULL ,
    
[IndexNum] [float] NOT NULL ,
    
[NormID] [int] NOT NULL 
ON [PRIMARY]

ALTER TABLE [dbo].[Index] ADD 
    
CONSTRAINT [FK_Index_Norm] FOREIGN KEY 
    (
        
[NormID]
    ) 
REFERENCES [dbo].[Norm] (
        
[NormID]
    )


norm和index为1对多的关系,norm的主键为normid(int not null identity),index的主键为indexid(int not null identity),normid为index表的外键

下面给出解决方案,最简单的方法就是修改linq to sql的设计器生成的代码

只需要在相应的Property上加上XmlElementAttribute就可以,由于代码太长,这里我只列出自己建立的linq to sql实体和DataContext

public class NormDataContext : DataContext
    
{

        
public Table<Norm> Norms;

        
public NormDataContext(IDbConnection connection) : base(connection) { }

        
public NormDataContext(string connection) : base(connection) { }

    }

    [Table(Name 
= "dbo.Norm")]
    
public class Norm
    
{
        
private int _NormID;

        
private string _TradeName;

        
private System.DateTime _SubmittedDate;

        
private EntitySet<Index> _Index;
        
public Norm()
        
{
            
this._Index = new EntitySet<Index>(new Action<Index>(this.attach_Index), new Action<Index>(this.detach_Index));
            
        }

        [XmlIgnore]
        [Column(Storage 
= "_NormID", AutoSync = AutoSync.OnInsert, DbType = "Int NOT NULL IDENTITY", IsPrimaryKey = true, IsDbGenerated = true)]
        
public int NormID
        
{
            
get return _NormID; }
            
set { _NormID = value; }
        }

        [XmlElement(
"TradeName")]
        [Column(Storage 
= "_TradeName", DbType = "NVarChar(30) NOT NULL", CanBeNull = false)]
        
public string TradeName
        
{
            
get return _TradeName; }
            
set { _TradeName = value; }
        }

        [XmlElement(
"SubmittedDate")]
        [Column(Storage 
= "_SubmittedDate", DbType = "DateTime NOT NULL")]
        
public System.DateTime SubmittedDate
        
{
            
get return _SubmittedDate; }
            
set { _SubmittedDate = value; }
        }

        [XmlElement(
"Index")]
        [Association(Name 
= "Norm_Index", Storage = "_Index", OtherKey = "NormID")]
        
public EntitySet<Index> Index
        
{
            
get return _Index; }
            
set { _Index = value; }
        }

        
private void attach_Index(Index entity)
        
{
            entity.Norm 
= this;
        }


        
private void detach_Index(Index entity)
        
{
            entity.Norm 
= null;
        }


    
    }


    [Table(Name 
= "dbo.[Index]")]
    
public class Index
    
{
        
private int _IndexID;

        
private string _IndexName;

        
private double _IndexNum;

        
private int _NormID;

        
private EntityRef<Norm> _Norm;

        
public Index()
        
{
            
this._Norm = default(EntityRef<Norm>);
        }

        [XmlIgnore]
        [Column(Storage 
= "_IndexID", AutoSync = AutoSync.OnInsert, DbType = "Int NOT NULL IDENTITY", IsPrimaryKey = true, IsDbGenerated = true)]
        
public int IndexID
        
{
            
get{return _IndexID;}
            
set{_IndexID=value;}
        }

        [XmlElement(
"IndexName")]
        [Column(Storage 
= "_IndexName", DbType = "NVarChar(30) NOT NULL", CanBeNull = false)]
        
public string IndexName
        
{
            
get{return _IndexName;}
            
set{_IndexName=value;}
        }

        [XmlElement(
"IndexNum")]
        [Column(Storage 
= "_IndexNum", DbType = "Float NOT NULL")]
        
public double IndexNum
        
{
            
get{return _IndexNum;}
            
set{_IndexNum=value;}
        }

        [XmlIgnore]
        [Column(Storage 
= "_NormID", DbType = "Int NOT NULL")]
        
public int NormID
        
{
            
get{return _NormID;}
            
set{_NormID=value;}
        }

        [Association(Name 
= "Norm_Index", Storage = "_Norm", ThisKey = "NormID", IsForeignKey = true)]
        
public Norm Norm
        
{
            
get{return _Norm.Entity;}
            
set{_Norm.Entity=value;}
        }

    }

注意norm和index的构造函数的内容一定要有,否则会报NSERT 语句与 COLUMN FOREIGN KEY 约束冲突,另外把相应的需要反序列化的Property标记XmlElement,不需要的如NormID,IndexID标记XmlIgnore(这个对应数据表的自增长列,利用linq to sql的DataContext完成剩下的提交工作),看代码

                Norm norm = serializer.Deserialize(new XmlNodeReader(xn)) as Norm;         
                NormDataContext ndc 
= new NormDataContext("server=server;database=BlackJack;uid=sa;pwd=");
                StreamWriter sw 
= new StreamWriter(AppDomain.CurrentDomain.BaseDirectory+"/log1.txt"true); // Append
                ndc.Log = sw;
                ndc.Norms.InsertOnSubmit(norm);
                ndc.SubmitChanges();
                sw.Close();


后记

在完成这段代码前,我一直在网上找类似的解决方案,但比较少,也可能是我孤陋寡闻,是不是linq系列将来会考虑支持这种方式感觉微软在xml序列化/反序列化做的很好,提供了很多的Attribute,使我们不用编写代码就能够完成工作,这里我也是抛砖引玉,还望园子里的朋友不吝赐教,另外我也把我的问题提出来,我在反序列化net class中利用XmlRootAttribute加入了自己的namespace,可是总是无法正确的反序列化,不知道怎么回事?

源代码

1 修改linq设计器生成的代码的例子

2 自己写的例子