青蛙煮酒论英雄 坐井观天话春秋

导航

基于Grove实践ORM的感悟

名词解释

  • 数据访问层:基于逻辑分层(Layer)的应用程序中直接与数据库交互的应用程序代码。
  • 业务实体:应用程序中业务数据的载体,一般有DataSetxml文本、自定义类等表现形式。(详细信息
  • 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的解决方案,它保护工程DALDataEntity,前者是数据访问层,后者是业务实体层。

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

建立业务实体

        本实例使用自描述自定义类作为业务实体类。所谓自描述,就是在该类中用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暂时不支持直接生成业务实体集合类,好在这部分工作很简单(写完一个后用copyreplace就能搞定),代码如下(为使代码简单只实现了集合最基本的方法):

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功能最重要的两个接口是IObjectOperatorIObjectQuery,前者为业务实体提供InsertUpdateDelete的功能,后者主要负责业务实体的查询。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所需的配置信息只能通过应用程序配置文件进行设置,不能用属性直接设置,不利于进行单元测试。
  • ArrayListDataSet形式返回数据实体集合,最好再能以CollectionBase的形式返回数据实体集合,这有利于操作强类型的数据实体集合。
  • 支持自动生成强类型的数据实体集合。
  • 能够在VS英文环境下生成代码文件。

参考资料

posted on 2004-03-01 20:41  青蛙煮酒  阅读(3234)  评论(4编辑  收藏  举报