您的位置:首页 > 其它

基于Grove实践ORM的感悟

2004-03-01 20:41 288 查看

名词解释

数据访问层:基于逻辑分层(Layer)的应用程序中直接与数据库交互的应用程序代码。

业务实体:应用程序中业务数据的载体,一般有DataSet、xml文本、自定义类等表现形式。(详细信息

ORM:是Object Relation Mapping的所写。通俗的讲就是要建立业务实体与关系数据库的映射关系。(详细信息

Grove Develop Kit

Grove Develop Kit是一套由国人开发的免费的数据持久层及相关工具,它包括Grove Develop Component和Grove Tool Kit 两部分:
Grove Develop Component是一套基于.NET的可重用开发组件,为开发人员提供一个数据持久层并提供多种ORM方式,另外它也支持传统的ADO.Net式的数据库访问方式。
Grove Tool Kit是基于微软VS的外接程序。通过使用Grove Tool Kit,开发人员能够在VS环境中直接从数据库表生成相应的业务实体类,极大的提高开发效率。

应用实例

说明

本实例旨在说明Grove的应用要点,未提供操作细节。如果需要全部代码,请到这里下载。

创建环境

1) 在Sql Server数据库中建立一个名为”Test”的数据库并建立如下两张表及对应关系(本实例所使用的业务数据类型非常简单,事实上Grove支持各种数据类型):

表名

字段

Customer

CustomerID char(36) PK
Name varchar(50)

Addresse

AddressID char(36) PK
CustomerID char(36) FK
Location varchar(200)

2) 在VS中建立一个TestGrove的解决方案,它保护工程DAL和DataEntity,前者是数据访问层,后者是业务实体层。

3) 通过菜单”工具-Grove Tool Kit”打开Grove的操作界面。通过点击该界面工具栏的”Set connection string”按钮来配置Grove的数据库操作环境。配置完成后连接数据库可看到Address和Customer这两张表。(工具栏第二个按钮用于设置数据库连接字符串,第一个按钮用于连接数据库,第三个按钮用于创建业务实体类)




建立业务实体

本实例使用自描述自定义类作为业务实体类。所谓自描述,就是在该类中用Attribute的形式包含了与数据库的映射关系。个人认为此种方式容易理解和维护。

4) 在Grove界面选择Address表,点击代码生成按钮将得到以下代码:

[DataTable("Address")]

public class Address

{

String _AddressID;

String _CustomerID;

String _Location;

[KeyField("AddressID")]

public String AddressID

{

get{return this._AddressID;}

set{this._AddressID=value;}

}

[ForeignKeyField("CustomerID")]

public String CustomerID

{

get{return this._CustomerID;}

set{this._CustomerID=value;}

}

[DataField("Location")]

public String Location

{

get{return this._Location;}

set{this._Location=value;}

}

}

注意:Grove对关键字字段进行映射时会将该字段看成自增类型。本实例未使用自增ID做关键字,所以需要手动调整。调整后AddressID属性的代码如下:

[KeyField("AddressID",KeyType = UniqueIDType.OtherDefinition)]

public String AddressID

{

get{return this._AddressID;}

set{this._AddressID=value;}

}

用同样的方法得到Customer业务实体类,代码如下:

[DataTable("Customer")]

public class Customer

{

String _CustomerID;

String _Name;

[KeyField("CustomerID",KeyType = UniqueIDType.OtherDefinition)]

public String CustomerID

{

get{return this._CustomerID;}

set{this._CustomerID=value;}

}

[DataField("Name")]

public String Name

{

get{return this._Name;}

set{this._Name=value;}

}

}

5) 生成业务实体强类型集合类:Grove暂时不支持直接生成业务实体集合类,好在这部分工作很简单(写完一个后用copy+replace就能搞定),代码如下(为使代码简单只实现了集合最基本的方法):

public class AddressCollection : CollectionBase

{

public Address this[Int32 index]

{

set

{

this.List[index] = value;

}

get

{

return (Address)this.List[index];

}

}

public Int32 Add(Address value)

{

return this.List.Add(value);

}

}

public class CustomerCollection : CollectionBase

{

public Customer this[Int32 index]

{

set

{

this.List[index] = value;

}

get

{

return (Customer)this.List[index];

}

}

public void Add(Customer value)

{

this.List.Add(value);

}

}

6) 修改Customer类,为其添加一个Addresses的属性,访问该属性可获取Customer对象对应全部Address。添加如下代码:

private AddressCollection _addresses;

public AddressCollection Addresses

{

get

{

if(this._addresses == null)

this._addresses = new AddressCollection();

return this._addresses;

}

}

建立数据访问层

7) ORM功能:Grove 提供ORM功能最重要的两个接口是IObjectOperator和IObjectQuery,前者为业务实体提供Insert、Update和Delete的功能,后者主要负责业务实体的查询。Grove没有提供直接实现IObjectOperator接口的public的类型,但我们可以通过ObjectOperatorFactory这个工厂类来得到一个实现了IObjectOperator接口的实例。代码如下:

public class CustomerDB

{

//插入一个Customer对象

public static void Insert(Customer customer)

{

IObjectOperator objectOperator =

ObjectOperatorFactory.GetObjectOperator();

objectOperator.BeginTranscation();//开始事务

try

{

//插入Customer对象与数据库有映射关系的属性值

objectOperator.InsertObject(customer);

//插入Customer对象对应的Address对象

objectOperator.InsertObjects(customer.Addresses);

objectOperator.Commit();//提交事务

}

catch

{

objectOperator.Rollback();//回滚事务

throw;

}

finally

{

objectOperator.Dispose();

}

}

//更新一个Customer对象

public static void Update(Customer customer)

{

IObjectOperator objectOperator =

ObjectOperatorFactory.GetObjectOperator();

objectOperator.BeginTranscation();

try

{

//更新Customer

objectOperator.UpdateObject(customer);

//删除Customer对象对应的Address

objectOperator.RemoveChildObjects(

customer.CustomerID, typeof(Address));

//插入Customer对象对应的Address对象

objectOperator.InsertObjects(customer.Addresses);

objectOperator.Commit();

}

catch

{

objectOperator.Rollback();

throw;

}

finally

{

objectOperator.Dispose();

}

}

//删除一个Customer对象

public static void Remove(Customer customer)

{

IObjectOperator objectOperator =

ObjectOperatorFactory.GetObjectOperator();

objectOperator.BeginTranscation();

try

{

//删除Customer对象

objectOperator.RemoveObject(customer);

//删除Customer对象对应的Address

objectOperator.RemoveChildObjects(

customer.CustomerID,typeof(Address));

objectOperator.Commit();

}

catch

{

objectOperator.Rollback();

throw;

}

finally

{

objectOperator.Dispose();

}

}

//通过查询条件获取Customer集合

public static CustomerCollection ReadCustomers(String filter)

{

IObjectOperator objectOperator =

ObjectOperatorFactory.GetObjectOperator();

IObjectQuery objectQuery =

objectOperator.NewQuery(typeof(Customer));

objectQuery.Filter = filter;//设置查询条件

CustomerCollection customers =new CustomerCollection();

ArrayList alCustomers;

try

{

alCustomers = objectQuery.Execute(typeof(Customer));

foreach(Object oCustomer in alCustomers)

{

Customer customer = (Customer)oCustomer;

//读取Customer对应的Address集合

ArrayList alAddresses = new ArrayList();

objectOperator.RetrieveChildObjects(

customer.CustomerID,alAddresses,typeof(Address));

foreach(Object oAddress in alAddresses)

{

customer.Addresses.Add((Address)oAddress);

}

customers.Add(customer);

}

}

finally

{

objectOperator.Dispose();

}

return customers;

}

}

以上代码随便比较长,但结构比较清晰,思路基本都是创建ObjectOperator -> 开启事务 -> 操作数据库 -> 释放ObjectOperator,调用过程都使用了try来捕获异常。另外值得一提的是Grove返回数据实体集合的时候是以ArrayList形式保存的,需要进行一下类型转换,然后添加到自定义的业务实体集合种。

8) 使用SQL或存储过程:有时候上面这个数据访问类并不能完全满足我们的要求,比如我们需要知道当前数据库种Customer的数量,当然这可以通过Ado.net来实现,但用Grove来实现的话更简便,代码如下(添加到CustomerDB类种):

//读取所有Customer的数量

public static Int32 GetCustomerCount()

{

IDbOperator dbOperator = DbOperatorFactory.GetDbOperator(

Grove.AppSettingManager.getAppSetting("DBConnString"));

try

{

return Convert.ToInt32(

dbOperator.ExecScalar("Select count(*) from customer"));

}

finally

{

dbOperator.Dispose();

}

}

意见和建议

Grove所需的配置信息只能通过应用程序配置文件进行设置,不能用属性直接设置,不利于进行单元测试。

以ArrayList或DataSet形式返回数据实体集合,最好再能以CollectionBase的形式返回数据实体集合,这有利于操作强类型的数据实体集合。

支持自动生成强类型的数据实体集合。

能够在VS英文环境下生成代码文件。

参考资料

Grove站点

实践ORM,创建基于Grove的.NET应用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: