读取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
<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
{
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
{
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使用
首先给出表结构
[NormID] [int] IDENTITY (1, 1) NOT NULL ,
[TradeName] [nvarchar] (30) NOT NULL ,
[SubmittedDate] [datetime] NOT NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[Index] (
[IndexID] [int] IDENTITY (1, 1) NOT NULL ,
[IndexName] [nvarchar] (30) NOT 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 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完成剩下的提交工作),看代码
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,可是总是无法正确的反序列化,不知道怎么回事?
源代码
2 自己写的例子