A Data Access Layer to persist business objects using attributes and reflection - Part I [无常译]

A Data Access Layer to persist business objects using attributes and reflection - Part I
By
xicoloko

Persistance to business objects through attributes and reflection 

无常译
 
目录:
第一部分
第二部分
第三部分

前言

从使用.NET(Beta 1 Release版本之后)开始,我开发过一些的使用MS Access或SQL Server的数据应用程序。ADO.NET的确是比ADO或OLEDB简单得多。我只需要一个打开的连接,就可以通过一个DataSet或DataReader从数据库中取数据。你可以根据需要选择合适的方法来检索数据库中的数据。

我知道强类型DataSet的强大功能,并且可以节省时间,但是我更喜欢用我的方法来进行数据库编程。我喜欢用一个类,调用这个类的方法就可以对其进行更新。这样一来,如果我需要往数据库中添加一行数据,只需要创建一个对象的实例并设置它的属性,再调用update方法。就这么简单!!

但这种编程方法让我输入很多代码,比如business object 类、更新数据和读数据的代码。开始的时候我没使用存储过程来更新数据,所以我必需为每个business object写一段SQL语句来更新数据表中的数据。只要我一改变数据或是business design,就不得不重复做这些工单调乏味的作。

我的解决方案

在我的方案中,首先要创建一个简单的类(DALQueryBuilder,代码在本系列的最后一篇文章中),它可以让我在更新对象的时候不用输入任何代码。我所要做的仅仅是给每列赋值,这个类就可以生成相应的SQL语句。在我完成之后我会高兴上一个礼拜...

当我开始用SQL Server代替Access的时候我就高兴不起来了。我不应该使用纯粹的SQL语句来更新我的对象,我只好使用存储过程。痛苦的日子又来了...我不得不创建上打的SQL参数来更新我的对象。令人厌烦的工作又开始了...

我注意到我可以写一个简单的类来生成这些参数,就像一个SQL语句生成器。虽然这个方法将只需要写很少的代码,但任何时候当我的方案改变时,都要回顾一下更新的代码。

我想到一个创建一些类来声明持久化到数据库中的方法。同样的使用另一些特性来声明属性如何影射到表中的列。以后改变方案的时候我只需需要修改business object类。

为了方便阅读,我将这篇文章分作3个部分。第一部分将解释怎样用特性来描述一个business class,第二部分讲怎么取得这些描述信息,最后一部分我将要给你展示完成的方案。

本方案仅在SQL Server7.0下测试通过。如果谁发现在Access下不能工作请告诉我。

第一部分:特性

使用特性是给程序集、类、属性、方法和字段添加声明信息的一种方法。有一些特性已经包含在.NET Framework中,但你也可以创建自己的特性。

我使用特性来声明把一个类存储到数据库中的方法。一个类中的属性可以被持久化也可以使用存储过程把数据存储到数据库中。我在类的属性中使用特性来声明和数据表中列的对应关系。列可以是简单的数据字段、unique键或外键。为了更好的理解特性,我推荐阅读.NET帮助文档或是James T. Johnson写的这篇文章(http://www.codeproject.com/csharp/dotnetattributes.asp)。

如何创建自定义特性?

这非常简单。你可以从System.Attribute类派生一个新类。按照命令的约定,自定义特性类最好使用Attribute后缀。在你创建自定义特性时,可以使用一个特性来宣告怎样使用,使用在类上?还是属性上?是否允许重复使用等等。

现在来看一段代码。这是一个用来描述Business Object类的特性。

using  System;
using  System.Data;

namespace  DAL
{

    [AttributeUsage(AttributeTargets.Property)]
    
public   class  BaseFieldAttribute : Attribute
    {
        
string  columnName;

        
public  BaseFieldAttribute( string  columnName)
        {
            
this .columnName  =  columnName;

        }

        
public   string  ColumnName
        {
            
get  {  return  columnName;  }
            
set  { columnName  =  value; }
        }

    }


    [AttributeUsage(AttributeTargets.Property)]
    
public   class  DataFieldAttribute : BaseFieldAttribute
    {
        DbType dbType 
=  DbType.String;
        
int     size    =   0 ;


        
public  DataFieldAttribute( string  columnName) :  base (columnName)
        {

        }

        
public  DbType Type
        {
            
get  {  return  dbType;  }
            
set  { dbType  =  value; }
        }

        
public   int  Size
        {
            
get  {  return  size;  }
            
set  { size  =  value; }
        }
    };

    [AttributeUsage(AttributeTargets.Property)]
    
public   class  KeyFieldAttribute : BaseFieldAttribute
    {
        
public  KeyFieldAttribute( string  columnName) :  base (columnName)
        {

        }
    };

    [AttributeUsage(AttributeTargets.Property)]
    
public   class  ForeignKeyFieldAttribute : BaseFieldAttribute
    {
        
public  ForeignKeyFieldAttribute( string  columnName) :  base (columnName)
        {

        }

    };

    [AttributeUsage(AttributeTargets.Class 
|  AttributeTargets.Struct)]
    
public   class  DataTableAttribute : Attribute
    {
        
string  tableName;
        
string  updateStoredProcedure    =   "" ;

        
public  DataTableAttribute( string  tableName)
        {
            
this .tableName  =  tableName;
        }


        
public   string  TableName
        {
            
get  {  return  tableName;  }
            
set  { tableName  =  value; }
        }


        
public   string  UpdateStoredProcedure
        {
            
get  {  return  updateStoredProcedure;  }
            
set  { updateStoredProcedure  =  value; }
        }
    }
}

 

像你所看到的一样,在每个类都有和个AttributeUsage特性,它表示此特性可以在什么地方使用。

如何使用特性来声明类?

假设你有一个程序,需要存储顾客和联系人的信息。在OO设计中,我们先要设计一个Person类,联系为就是Person加上地址和联系的信息,顾客是联系为加上购买的统计。同样,一个顾客类也是依赖于Persons类。我知道这样很乏味,但我还是在要文章的其余部分用到它。

using  System;
using  System.Data;
using  DAL;

namespace  TestApp
{
    
public   class  Person
    {
        
string  name  =   "" ;
        
int  age  =   0 ;
        
int  id  =   0 ;


        [KeyField(
" id " )]
        
public   int  Id
        {
            
get  {  return  id;  }
            
set  { id  =  value; }
        }

        [DataField(
" name " , Size = 50 )]
        
public   string  Name
        {
            
get  {  return  name;  }
            
set  { name  =  value; }
        }

        [DataField(
" age " )]
        
public   int  Age
        {
            
get  {  return  age;  }
            
set  { age  =  value; }
        }

        
public   override   string  ToString()
        {
            
return   string .Format( " <PERSON>{0}, {1} years old " , Name, Age);
        }
    }


    [DataTable(
" contact " , UpdateStoredProcedure = " sp_UpdateContact " )]
    
public   class  Contact : Person
    {
        
string  phone  =   "" ;
        
string  email  =   "" ;
        
string  address  =   "" ;
        
string  address2  =   "" ;
        
string  city  =   "" ;
        
string  postalCode  =   "" ;
        
string  state  =   "" ;
        
string  country  =   "" ;

        [DataField(
" phone " , Size = 20 )]
        
public   string  Phone
        {
            
get  {  return  phone;  }
            
set  { phone  =  value; }
        }

        [DataField(
" email " , Size = 80 )]
        
public   string  Email
        {
            
get  {  return  email;  }
            
set  { email  =  value; }
        }

        [DataField(
" address " , Size = 80 )]
        
public   string  Address
        {
            
get  {  return  address;  }
            
set  { address  =  value; }
        }


        [DataField(
" address2 " , Size = 80 )]
        
public   string  Address2
        {
            
get  {  return  address2;  }
            
set  { address2  =  value; }
        }


        [DataField(
" city " , Size = 50 )]
        
public   string  City
        {
            
get  {  return  city;  }
            
set  { city  =  value; }
        }


        [DataField(
" postalCode " , Size = 20 )]
        
public   string  PostalCode
        {
            
get  {  return  postalCode;  }
            
set  { postalCode  =  value; }
        }


        [DataField(
" state " , Size = 4 )]
        
public   string  State
        {
            
get  {  return  state;  }
            
set  { state  =  value; }
        }


        [DataField(
" country " , Size = 50 )]
        
public   string  Country
        {
            
get  {  return  country;  }
            
set  { country  =  value; }
        }


        
public   override   string  ToString()
        {
            
return   string .Format( " <Contact>{0} - {1} from {2} " , Id, Name, Country);
        }
    }


    
public   enum  CustomerRelationship { Family, Friend, Other };

    [DataTable(
" customerDependent " , UpdateStoredProcedure = " sp_UpdateCustomerDependent " )]
    
public   class  CustomerDependent : Person
    {
        
int  customerId  =   0 ;
        CustomerRelationship relationship 
=  CustomerRelationship.Family;

        
protected  CustomerDependent()
        {

        }

        
public  CustomerDependent( int  customerId)
        {
            
this .customerId  =  customerId;
        }

        [ForeignKeyFieldAttribute(
" customerId " )]
        
public   int  CustomerId
        {
            
get  {  return  customerId;  }
            
set  { customerId  =  value; }
        }

        [DataFieldAttribute(
" relationship " )]
        
public  CustomerRelationship Relationship
        {
            
get  {  return  relationship;  }
            
set  { relationship  =  value; }
        }
    }




    
public   enum  CustomerStatus { Active, Inactive };

    [DataTable(
" customer " , UpdateStoredProcedure = " sp_UpdateCustomer " )]
    
public   class  BaseCustomer : Contact
    {
        CustomerStatus status 
=  CustomerStatus.Active;
        Decimal totalPurchased 
=  0M;
        
int  numberOfPurchases   =   0 ;
        DateTime dateRegistered 
=  DateTime.Now;

        [DataField(
" status " )]
        
public  CustomerStatus Status
        {
            
get  {  return  status;  }
            
set  { status  =  value; }
        }


        [DataField(
" totalPurchased " )]
        
public  Decimal TotalPurchased
        {
            
get  {  return  totalPurchased;  }
            
set  { totalPurchased  =  value; }
        }

        [DataField(
" numberOfPurchases " )]
        
public   int  NumberOfPurchases
        {
            
get  {  return  numberOfPurchases;  }
            
set  { numberOfPurchases  =  value; }
        }

        [DataField(
" dateRegistered " )]
        
public  DateTime DateRegistered
        {
            
get  {  return  dateRegistered;  }
            
set  { dateRegistered  =  value; }
        }


        
public   override   string  ToString()
        {
            
return   string .Format( " <Customer>{0} - {1} from {2}, registered in {3}. " +
                                 
"  #{4} purchases spending a total of $ {5} " ,
                          Id,
                          Name,
                          Country,
                          DateRegistered,
                          NumberOfPurchases,
                          TotalPurchased);
        }

    }

    
public   class  Customer : BaseCustomer
    {

        ArrayList dependents 
=   null ;

        
public  ArrayList Dependents
        {
            
get
            {
                
if  (dependents  ==   null )
                {
                    DAL dal 
=   new  DAL();
                    dependents 
=  dal.GetCustomerDependents( this );
                }

                
return  dependents;
            }
        }

        
public  CustomerDependent NewDependent()
        {
            
return   new  CustomerDependent(Id);
        }

        
public  Decimal PurchaseMedia
        {
            
get  {  return  TotalPurchased  /  NumberOfPurchases; }
        }


    }

}

Person类是我们所有类的基类,这是唯一一个没有DataTable特性的类,因为它不需要被持久化。只有Contacts、CustomerDependents 和Customers保存在数据库中。但每个派生类都拥有Person类中已经定义的一些列。在这个例子中,ID属性是一int、自动增长类型的主键。属性名为50个字符以内的字符串等竺。这里仅定义了属性CustomerDependent::CustomerId为外键。

在SQL Server中创建这些类,仅需要在查询分析器在执行下面的SQL脚本。

if   exists  ( select   *   from  sysobjects  where  id  =   object_id (N ' [dbo].[contact] '
           
and   OBJECTPROPERTY (id, N ' IsUserTable ' =   1 )
drop   table   [ dbo ] . [ contact ]
GO

if   exists  ( select   *   from  sysobjects  where  id  =   object_id (N ' [dbo].[customer] '
           
and   OBJECTPROPERTY (id, N ' IsUserTable ' =   1 )
drop   table   [ dbo ] . [ customer ]
GO

if   exists  ( select   *   from  sysobjects  where  id  =   object_id (N ' [dbo].[customerDependent] '
           
and   OBJECTPROPERTY (id, N ' IsUserTable ' =   1 )
drop   table   [ dbo ] . [ customerDependent ]
GO

CREATE   TABLE   [ dbo ] . [ contact ]  (
    
[ id ]   [ int ]   IDENTITY  ( 1 1 NOT   NULL  ,
    
[ name ]   [ varchar ]  ( 50 NOT   NULL  ,
    
[ age ]   [ int ]   NOT   NULL  ,
    
[ address ]   [ varchar ]  ( 80 NOT   NULL  ,
    
[ postalCode ]   [ varchar ]  ( 20 NOT   NULL  ,
    
[ phone ]   [ varchar ]  ( 20 NOT   NULL  ,
    
[ email ]   [ varchar ]  ( 80 NOT   NULL  ,
    
[ address2 ]   [ varchar ]  ( 80 NOT   NULL  ,
    
[ city ]   [ varchar ]  ( 50 NOT   NULL  ,
    
[ state ]   [ varchar ]  ( 4 NOT   NULL  ,
    
[ country ]   [ varchar ]  ( 50 NOT   NULL
ON   [ PRIMARY ]
GO

CREATE   TABLE   [ dbo ] . [ customer ]  (
    
[ id ]   [ int ]   IDENTITY  ( 1 1 NOT   NULL  ,
    
[ name ]   [ varchar ]  ( 50 NOT   NULL  ,
    
[ age ]   [ int ]   NOT   NULL  ,
    
[ address ]   [ varchar ]  ( 80 NOT   NULL  ,
    
[ postalCode ]   [ varchar ]  ( 20 NOT   NULL  ,
    
[ phone ]   [ varchar ]  ( 50 NOT   NULL  ,
    
[ email ]   [ varchar ]  ( 80 NOT   NULL  ,
    
[ address2 ]   [ varchar ]  ( 80 NOT   NULL  ,
    
[ city ]   [ varchar ]  ( 50 NOT   NULL  ,
    
[ state ]   [ varchar ]  ( 4 NOT   NULL  ,
    
[ country ]   [ varchar ]  ( 50 NOT   NULL  ,
    
[ totalPurchased ]   [ money ]   NOT   NULL  ,
    
[ numberOfPurchases ]   [ int ]   NOT   NULL  ,
    
[ dateRegistered ]   [ datetime ]   NOT   NULL  ,
    
[ status ]   [ smallint ]   NOT   NULL
ON   [ PRIMARY ]
GO

CREATE   TABLE   [ dbo ] . [ customerDependent ]  (
    
[ id ]   [ int ]   IDENTITY  ( 1 1 NOT   NULL  ,
    
[ name ]   [ varchar ]  ( 50 NOT   NULL  ,
    
[ customerId ]   [ int ]   NOT   NULL  ,
    
[ relationship ]   [ int ]   NOT   NULL  ,
    
[ age ]   [ int ]   NOT   NULL
ON   [ PRIMARY ]
GO


下一部分要讨论的内容

在下一篇文章中,我将要给你绍介反射件怎样帮助我们聚合更新数据库时需要的信息的。我将要
创建一个简单的应用程序,通过程序集中的类定义来生成创建表的SQL脚本。

 

原文地址:http://www.codeproject.com/cs/database/dal1.asp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值