下载源代码

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

前言

这是本系列最后一篇文章。在第一篇中我们知道了自给使用特性来给类添加声明信息。第二篇中我们已经知道如何使用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
 终于完成了这篇文章的翻译。
很多外文的文章,阅读没什么问题,意思都能看懂,但想用文字翻译流畅出来,却要费一番工夫了。
翻译得不好的地方欢迎指出。

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

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

    A Data Access Layer to persist business objects using attributes and reflection - Part II By xicolok ...

  2. Generic Data Access Layer泛型的数据访问层

    http://www.codeproject.com/Articles/630277/Generic-Data-Access-Layer-GDA-Part-I http://www.codeproje ...

  3. Generic Data Access Objects -范型DAO类设计模式

    Generic Data Access Objects 普通数据访问对象,这个是Hibernate官方网站上面的一个DAO类的设计模式,基于JDK5.0范型支持,文章地址如下: http://www. ...

  4. SAP Hybris和ABAP Netweaver里的DAO(Data access object)

    DAO在Hybris里的定义: A DAO (Data Access Object) is an interface to the storage back end system. DAOs stor ...

  5. 使用基于 Eclipse 插件框架的 ODA(Open Data Access)进行自定义数据驱动开发

    ODA 之所以能够有如此强的灵活性,是因为: 它提供了一套完整的接口,开发者可以自己去实现数据源的访问逻辑,使得数据源对数据使用者变得透明.只要开发者遵循编程规范,就可以对任何数据进行驱动. 它基于 ...

  6. 感觉 Data Access Application Block(DAAB) 里也有可能写得不太好的地方

    昨天下载了博客园的代码,里面有一个 Data\SqlServer.cs 我不清楚是不是 MS DAAB 里的原样文件.不过前面有声明如下: // =========================== ...

  7. 开发自己的Data Access Application Block[下篇]

    上接:[原创] 我的ORM: 开发自己的Data Access Application Block - Part I 4. Database 下面来介绍重中之重:Database,绝大部分的Data  ...

  8. Enterprise Library: Data Access Application Block配置文件分析篇

    Enterprise Library: Data Access Application Block配置文件分析篇 Enterprise Library提供了Configuration Console配 ...

  9. Dao设计模式(Data Access Object)

    目    录(本篇字数:1858) 介绍 通用Dao 一.Dao泛型接口 二.JavaBean 三.Dao接口实现类 四.单元测试 五.反射工具类 介绍 Dao设计模式(Data Access Obj ...

最新文章

  1. 使用Oracle 的 imp ,exp 命令实现数据的导入导出
  2. python批量改名_Python写个批量改名工具
  3. 怎么将计算机网络设置家庭,“windows无法在此计算机上设置家庭组”问题如何解决...
  4. 进阶的“车厘子自由”,进化的“淘宝特价版”
  5. beetlsql报错“请指定Sql类型“的解决方案
  6. 20161207py学习笔记
  7. ---WebCam网络摄像头7 cmos--yuv rgb , Format............:V4L2_PIX_FMT_YUYV
  8. Ubuntu下Python与C/C++混合编程
  9. java程序a-z b-y,请完成下列Java程序:对大写的26个英文字母加密,从键盘输入一个大写字母串,输出这个串加密后的结 - 赏学吧...
  10. 网易有数的“正确”使用方式——洞察数据中隐藏的故事
  11. SSH连接原理及ssh-key
  12. Typinator for mac(打字员)附注册码支持m1
  13. 上海行政区划经纬度地图_全国行政区划2017省市区完整版附经纬度及拼音
  14. linux加密狗复制克隆教程,Guardant Code/Guardant加密狗解密 复制 克隆 模拟
  15. wps分享变成小程序怎么变成文档_微信小程序使用腾讯文档打开文档
  16. 2017携程java后台开发工程师暑期实习生招聘面试经验分享
  17. JIEDU7种EMI电磁屏蔽材料介绍
  18. MyApps平台为政企数据保驾护航,筑牢办公安全防线
  19. linux 桌面 v2ex,程序员:他人笑我桌面太凌乱,我笑他人看不穿
  20. 调频广播如何发明的?

热门文章

  1. webpack 错误提示 Error: Can't resolve 'css-loader'或Error: Can't resolve 'style-loader'
  2. python连接redis sentinel集群
  3. iOS: 环信的推送
  4. 格密码教程(四):SVP和CVP,Hermite定理,Blichfeld定理和Minkowski定理
  5. Simulink之理想开关
  6. 51单片机按键控制数码管0~9_7种常见的51单片机时钟电路图
  7. 聊聊天,如果能重来,还干不干程序员?
  8. IDEA发布新版本,支持禅模式(免打扰)!
  9. 【插件发布】JAVA微服务框架,Jeecg-P3-Base-System 1.0.0 插件开源发布
  10. 绿色日期控件皮肤 My97 DatePicker