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

下载源代码

目录:
第一部分
第二部分
第三部分

前言

这是本系列最后一篇文章。在第一篇中我们知道了自给使用特性来给类添加声明信息。第二篇中我们已经知道如何使用System.Reflection namespace来取得声明信息。现在我们将要创建一个DAL库,可以根据任何对象自身的声明信息来实现持久化。

设计DAL库

在创建这个DAL库时,我打算将它设计成既支持SQL Serverdata provider ,也支持OldDbdata provider 。也可以扩展支持其它的data provider。我把这个库分成下面几个部分:

Utility classes
DALQueryBuilder类

在每一篇文章中已经提到过,这个类帮助构建更新对象到数据库中的SQL语句。

DALParameter类
一个普通的参数类,在存储过程中用到。

DALException类
这是一个System.Exception的派生类。在操作数据库操作出错时就会掏出此异常。我提供了有关出错的更多的信息。

Attribute 类

派生于System.Attribute 类,在第一篇中已经介绍过。

DAL itself

DALEngine 类

数据操作的抽象基类,但数据库操作更加容易。它拥有虚拟和抽象方法,不同的data provider会有不同的实现。大多的函数从数据库中返回DataSets、DataReaders,它提供方法来操作使用DAL特性标记过的对象。

DALSqlEngine 类

DALEngine的使用SQL Server的实现。 

详细分析DALEngine 类

这是DALEngine类的声明:

public   abstract   class  DALEngine : IDisposable
{
    
//
    
//  private data members
    
//
    IDbConnection conn        =   null ;
    
string  connectionString   =   "" ;
    ArrayList parameters 
=   new  ArrayList();
    
bool  canClose  =   true ;

    
//  constructor
     public  DALEngine( string  connectionString);
    
public   bool  CanClose;
    
public   string  ConnectionString;


    
protected  IDbConnection Connection;
    
protected  ArrayList Parameters;

    
public   void  Close();
    
public   void  Dispose();


    
//
    
//  methods that must be override with a specific data provider 
    
//  implementation please see the implementation of DALSqlEngine 
    
//  or DALOleDbEngine
    
//  
     protected   abstract  IDbConnection GetConnection();
    
protected   abstract  IDbCommand CreateCommand( string  spName);
    
public   abstract   void  ExecSP_DataSet( string  spName, DataSet dataSet, 
                                       
string  tableName);
    
public   abstract   void  ExecQuery_DataSet( string  query, DataSet dataSet, 
                                           
string  tableName);


    
//
    
//  related to stored procedure parameters
    
//
     public  DALParameter GetParameter( string  name);
    
void  UpdateOutputParameters(IDbCommand cmd);
    
public   void  AddParameter(DALParameter param);
    
public   void  ClearParameters();


    
//
    
//  for those that use stored procedures
    
//
     public  IDataReader ExecSP_DataReader( string  spName);
    
public  IDataReader ExecSP_DataReader( string  spName, 
                                         CommandBehavior behavior);
    
public   object  ExecSP_Scalar( string  spName);
    
public   int  ExecSP_NonQuery( string  spName);


    
//
    
//  methods for those that use plain SQL statements
    
//
     public  IDataReader ExecQuery_DataReader( string  query, 
                                            CommandBehavior behavior);
    
public  IDataReader ExecQuery_DataReader( string  query);
    
public   object  ExecQuery_Scalar( string  query);
    
public   int  ExecQuery_NonQuery( string  query);




    
//
    
//  Business objects methods
    
//
     public   static   object  CreateFromReader(IDataReader reader, Type objType);
    
public   object  RetrieveObject( object  keyValue, Type objType);
    
public   int  RetrieveChildObjects( object  foreignKeyValue, ArrayList objects,
                                    Type childType);
    
void  UpdateObjectSql( object  o, DataTableAttribute dataTable);
    
void  UpdateObjectStoredProcedure( object  o, DataTableAttribute dataTable);
    
public   void  UpdateObject( object  o);
    
public   void  UpdateObjects(IEnumerable enumObjects);
}

像你所想象中的一样,我们只需要关注最后的6个函数。其它的是很容易理解的,因为他们很容易实现。

CreateFromReader函数返回一个objType类型的对象的实例。当我们从数据库中取出数据时,就可以使用此函数来返回一个business object实例。它是一个静态函数,所以不需要建立数据库连接,只需要一个DataReader就可以读数据了。

RetrieveObject函数返回一个objType类型的对象的实例。这个实例的属性与表主键列与keyValue参数相等的唯一一行对应。这个函数在底层创建了有WHERE子句的SELECT语句。RetrieveChildObjects创建外键与foreignKeyValue 参数相等的 childType 类型的对象实例。创建的这些实体都被加到objects 参数中。

UpdateObject 和UpdateObjects 函数对数据库进行一个更新操作。更新可以使用储存过程(在此例中的类的DataTable 特性用UpdateStoreProcedure 属性来设置)或是普通SQL语句。二个私有方法UpdateObjectSql 和UpdateObjectStoredProcedure 负责执行这些更新操作。

这样做的好处

所有使用此DAL的都会使数据库编程更加容易。我强烈建议使用这个DAL,因为已经实现了重复冗长的任务,即使你没打算像我一样进行数据库编程。这次你不是必需用到,但以后至少会用到一次。

但是如果你开发数据库程序的时候用business objects ,这个库可以帮你节省很多时间和精力。这只有一个好处,就是在修改数据库时需要重新检查代码,这可以使用你自己的一个工具来完成,但利用在这篇文章的第二部分创建的工具可以完成相反的功能,即先修改business objects然后再根据business objects生成表。

为了让你更容易理解获取和更新对象,我在DAL库的代码中包含有一个样本的应用程序。这里有一些此应用程序的代码片断,你可以从中了解它的功能。

这个DAL库确实存在很多BUG,这也是我在这里发表这篇文章的原因之一。也许你会有更好的方法和代码来实现这个库的目的。有什么建议请告诉我,像我一样乐于改进这相库。 

因为我要使用SQL Server,可以我从DALSqlEngine派生了一个DAL类来实现这个样板应用程序。这是我的DAL类的代码:

public   class  DAL : DALSqlEngine
{
    
const   string  CONN_STRING  =   " server=localhost;uid=sa;pwd=;database=pubs " ;

    
public  DAL() :  base (CONN_STRING)
    {

    }

    
public  ArrayList GetCustomerDependents(Customer customer)
    {
        ArrayList result 
=   new  ArrayList();

        RetrieveChildObjects(customer.Id, result, 
typeof (CustomerDependent));

        
return  result;
    }

    
public   void  UpdateCustomerDependents(Customer customer)
    {
        UpdateObjects(customer.Dependents);
    }
}

是不是很简单?现在是这个应用程序的代码,仅仅插入/更新了contact/customers和customer从属的对象。

public   static   void  Main()
{

    DAL dal 
=   new  DAL();

    
try
    {

        Contact contact 
=   new  Contact();
        contact.Name 
=   " Joao Cardoso " ;
        contact.Age  
=   23 ;
        contact.Address 
=   " Av. Rio Branco, 202/121 " ;
        contact.Address2 
=   " Centro " ;
        contact.PostalCode 
=   " 09029-901 " ;
        contact.City 
=   " Sao Paulo " ;
        contact.State 
=    " SP " ;
        contact.Country 
=   " Brazil " ;

        dal.UpdateObject(contact);
        Console.WriteLine(contact);


        Contact joaoCardoso 
=  (Contact)dal.RetrieveObject( 1 typeof (Contact));
        joaoCardoso.Age
++ ;
        Console.WriteLine(joaoCardoso);
        Console.WriteLine(
"" );


        Customer customer 
=   new  Customer();
        customer.Name 
=   " Paul Noyter " ;
        customer.Age  
=   34 ;
        customer.Address 
=   " All St, 2202/2121 " ;
        customer.Address2 
=   " Downville " ;
        customer.PostalCode 
=   " 90931 " ;
        customer.City 
=   " Los Angeles " ;
        customer.State 
=    " CA " ;
        customer.Country 
=   " United States " ;
        customer.TotalPurchased 
+=   1900.87M ;
        customer.NumberOfPurchases
++ ;

        dal.UpdateObject(customer);


        Customer paul 
=  (Customer)dal.RetrieveObject( 1 typeof (Customer));
        Console.WriteLine(paul);

        paul.TotalPurchased 
+=  100M;
        paul.NumberOfPurchases
++ ;
        dal.UpdateObject(paul);

        
if  (paul.Dependents.Count  ==   0 )
        {
            CustomerDependent dependent 
=  paul.NewDependent();
            dependent.Name 
=   " Marie Noyter " ;
            dependent.Age 
=   31 ;
            paul.Dependents.Add(dependent);


            dependent 
=  paul.NewDependent();
            dependent.Name 
=   " Mark Noyter " ;
            dependent.Age 
=   10 ;
            paul.Dependents.Add(dependent);


            dependent 
=  paul.NewDependent();
            dependent.Name 
=   " Claudia Snorg " ;
            dependent.Age 
=   32 ;
            dependent.Relationship 
=  CustomerRelationship.Friend;
            paul.Dependents.Add(dependent);

            dal.UpdateCustomerDependents(paul);
        }
        
else
        {
            Console.WriteLine(
" Dependents of {0} " , paul.Name);

            
foreach (CustomerDependent dependent  in  paul.Dependents)
            {
                Console.WriteLine(
" <Dependent>{0} - {1} [{2}] " , dependent.Id, 
                                  dependent.Name, dependent.Relationship);
                dependent.Relationship 
=  CustomerRelationship.Family;
            }

            dal.UpdateCustomerDependents(paul);
        }

    }
    
finally
    {
        dal.Dispose();
    }
}

局限性

当然这个库也有一定的局限性。部分可以通过扩展DAL或attributes来解决。如果你不使用自动增长列,那么就必须使用存储过程来更新数据。这是因为DAL不能断定这个对象是更新还是插入到数据库。你可以扩展DAL来检测已经的特性或接口来提供需要的信息;一般不支持复合主键的表;性能要比直接使用SqlCommand要差些,但是你会有更多的时间来使用business classes替换数据库编程;我没有实现使用存储过程从数据库中检索对象,但这很容易实现。

结论

在.NET世界里,有很多途径来进行数据库编程。Visual Studio .NET允许你使用强类型的DataSet,可以节省你很多生成代码的时间。使用DataSets也一点不错,但是我更喜欢用business objects。当我开始开发这样的一样类的时间,我就可以知道写其它的类需要多长时间。这个库介绍到这里,你应该知道了怎么完善它来开发使用business objects的数据库应用程序。

---------------------------------------------------by wuchang
 终于完成了这篇文章的翻译。
很多外文的文章,阅读没什么问题,意思都能看懂,但想用文字翻译流畅出来,却要费一番工夫了。
翻译得不好的地方欢迎指出。

转载于:https://www.cnblogs.com/wuchang/archive/2006/04/12/373442.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值