1.什么是ORM?
ORM,即Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在具体的操作业务对象的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了。
对象关系映射(ORM)提供了概念性的、易于理解的模型化数据的方法。ORM方法论应当基于三个核心原则:
简单:以最基本的形式建模数据。
传达性:数据库结构被任何人都能理解的语言文档化。
精确性:基于数据模型创建正确标准化了的结构。
基于三项原则,一方面,建模者通过收集来自那些熟悉应用程序但不熟练的数据建模者的人的信息开发企业实体模型,业务实体的设计者也可以在完全脱离数据结构构架的基础上应用这些业务实体并构筑企业的应用系统。另一方面我们可以将那些简单而又枯草地SQL语句完全忘却,在ORM的构架中,它们对于建模者应用来说完全是多余的。
2.Snake.Net中ORM的特点:
Snake.Net中的数据映射是基于DataSet对象的。DataSet 对象是 Microsoft .NET 框架中数据访问的关键部分,是可保存表、视图和关系的内存中对象。Snake.Net中的数据映射是通过定义一个描述业务实体的DataSet(如图1.1),将其所需要应用到的数据表结构定义(表以及表与表之间的关系)进行描述。然后通过构建一个业务实体类(Class)并将类内特定域(Field)映射到DataSet中相对应数据表内的字段而实现的。
图1.1 利用Visual Sudio.Net生成的DataSet结构定义
使用DataSet构架的优点有哪些?
1) 业务实体与DataSet之间可以方便的进行相互转化,这种转化不仅实现于单个的业务实体,也可以对实现IList接口的业务实体集合进行批量转化。
2) 通过DataSet可以实现对XML序列化的完全支持,也就是能够实现对IXmlSerializable接口的支持,从而便于分布式部署,以及对SOAP的支持。
3) DataSet作为Microsoft ADO.Net重要的组成部分之一,通过Visual Studio.Net内置的工具就可以方便的生成DataSet结构的定义,从而有效地提高了开发的速度。
4) DataSet对象是完全独立于数据库产品的对象,这也就为Snake.Netd对支持不同厂商不同版本的各类数据库产品的实现完全成为可能。
3.从构建一个业务实体开始
以上图1.1定义的DataSet为例,让我们从构建一个简单的Customer类开始。
[UniqueTypeDeclare(typeof(StringToken))]
public class Customer:DataBindObject
{
#region declare
[DataColumnMap("CustomerID")]
private string _id;
[DataColumnMap("CompanyName")]
private string _companyName;
[DataColumnMap("ContactName")]
private string _contactName;
[DataColumnMap("ContactTitle")]
private string _contactTitle;
[DataColumnMap("Address")]
private string _address;
[DataColumnMap("City")]
private string _city;
[DataColumnMap("Region")]
private string _region;
[DataColumnMap("PostalCode")]
private string _postalCode;
[DataColumnMap("Country")]
private string _country;
[DataColumnMap("Phone")]
private string _phone;
[DataColumnMap("Fax")]
private string _fax;
#endregion
#region constructors
public Customer(string id):this(true)
{
//check for arguments
if (id == null) throw new ArgumentNullException("id");
_id = id;
}
protected Customer(bool create):base(create)
{
}
#endregion
#region properties
public override IUnique Unique{
get{
return new StringToken(Id);
}
}
public string Id{
get{return _id = (string)GetDataField(_id);}
}
public string CompanyName{
get{return _companyName = (string)GetDataField(_companyName);}
set{_companyName = (string)SetDataField(_companyName, value);}
}
public string ContactName{
get{return _contactName = (string)GetDataField(_contactName);}
set{_contactName = (string)SetDataField(_contactName, value);}
}
public string ContactTitle
{
get{return _contactTitle = (string)GetDataField(_contactTitle);}
set{_contactTitle = (string)SetDataField(_contactTitle, value);}
}
public string Address
{
get{return _address = (string)GetDataField(_address);}
set{_address = (string)SetDataField(_address, value);}
}
public string City{
get{return _city = (string)GetDataField(_city);}
set{_city = (string)SetDataField(_city, value);}
}
public string Region
{
get{return _region = (string)GetDataField(_region);}
set{_region = (string)SetDataField(_region, value);}
}
public string PostalCode
{
get{return _postalCode = (string)GetDataField(_postalCode);}
set{_postalCode = (string)SetDataField(_postalCode, value);}
}
public string Country{
get{return _country = (string)GetDataField(_country);}
set{_country = (string)SetDataField(_country, value);}
}
public string Phone{
get{return _phone = (string)GetDataField(_phone);}
set{_phone = (string)SetDataField(_phone, value);}
}
public string Fax{
get{return _fax = (string)GetDataField(_fax);}
set{_fax = (string)SetDataField(_fax, value);}
}
#endregion
}
通过上述代码我们可以发现定义一个业务实体类的过程是非常简单。
首先,定义一个描述Customer类的xsd文件(如图1.1),将其命名为xsCustomer.xsd并存放在Customer.cs相同的目录下,同时在属性窗口将其生成操作设置为嵌入的资源。
然后,编写一个继承于DataBindObject类的Customer类。接着,只需要为其Field声明DataColumnMapAttribute属性,将字段一一映射到对应的数据表字段,并通过编写属性(Property)和方法(Method)来控制对Field的访问。
最后,我们还要做的是,实现一个IUnique Unique{get;}的属性(Property),并且在类上声明这个IUnique类的具体实例名。
这样一个简单的Customer类的构建工作就大功告成了。
同时,通过上述代码的观察,我们或许存在这样一些疑问?
1) 为什么使用Field映射而不直接使用Property映射内?那是应为作者认为通过Property的控制可以使业务实体的应用更加灵活(有些映射的字段是并不是一定要公开的、可以转化成另一种形式公开或者是只读等)。
2)属性的定义形式是:
public string CompanyName{
get{return _companyName = (string)GetDataField(_companyName);}
set{_companyName = (string)SetDataField(_companyName, value);}
}
而不是一种更简单的形式:
public string CompanyName{
get{return _companyName = _companyName;}
set{_companyName = value;}
}
那是因为Customer类所继承的DataBindObject类是一个相当复杂的类,它有一些内在地机制用于控制并发处理,内置事件,通讯机制,调用GetDataField和SetDataField函数可以更好的实现这些功能,而不需要额外的代码。
3)我们发现当表的字段比较多时,需要手工编写的代码量还是相当的大。但是不用担心,这样的简单重复劳动我们完全可以交给CodeSmith之类的软件,用模板生成,当Snake.Net正式发布的时候,将同时发布CodeSmith的构造模板。那样我们只需要通过简单的按几下鼠标就可以完成整个业务实体类的创建工作。
4)为什么要实现IUnique Unique{get;}的属性?这个属性继承之IObject接口,这是Snake.Net构架的一个最重要的接口,它所表示的意义是一个对象在整个系统中的唯一标识。换言之其就是对应于数据表中的主关键字。通过这个特殊的属性,Snake.Net可以方便的实现对业务实体的查询。
5)上面的例子只是对于一个简单对象的应用,Snake.Net能够构建更复杂的对象呢,比如包含有多张子表?答案当然是肯定的,只是构造起来相对复杂一些。(当然这项艰巨的任务也可以教给CodeSmith先生去完成)只是限于篇幅,不在这里进行详细描述,我们可以通过Snake.Net自带的例子进行深入研究。