自动对象持久化 – 第一部分






3.06/5 (9投票s)
一种持久化业务实体的 API,
引言
作为一名经验丰富的开发人员,我们可能无法回答“我写了多少次以数据库为中心的、用于保存/检索/删除业务实体的代码?”这个问题。在我们的生活中,我们写过很多次以数据库为中心的的代码。有时,厌倦了,我们会想摆脱一遍又一遍地编写这种通用代码!
大多数业务应用程序都需要我们编写类似以下内容的以数据库为中心的的代码:
OleDbConnection connection = null; try { string strSql = "SELECT ID, Name, ... FROM tblCust;"; connection = // create connectoin OleDbCommand command = new OleDbCommand(strSql, connection); command.Parameters.Add("@ID", 1323); . . command.Parameters.Add("@Address", "First Lane"); OleDbDataReader reader = command.ExecuteReader(); if (reader.Read()) { // additional code } } catch { }
厌倦了编写这种持久化业务实体的相同逻辑后,工程师和研究人员开始思考一种方法来减少持久化业务实体的应用程序代码。将业务对象写入和从数据库表中读出、更新、删除(CRUD)的逻辑,在任何给定语言中看起来都是一个普遍的模式。也就是说,执行此类操作的代码在所有上下文中看起来都几乎相同。如果您希望向数据库表执行 INSERT 操作以持久化业务对象,那么您很有可能在一个全新的应用程序中编写相同的代码逻辑来持久化其他业务实体。
当看到这些模式一遍又一遍地出现时,研究人员正在考虑建立一个框架,通过提供 API 来实现相同的目标。我们已经看到了用于持久化业务实体的此类框架。NHibernate 就是一个例子,使用它可以最大限度地减少以数据库为中心的代码,从而也使我们的生活更轻松。
背景
2003 年,我开始使用 J2EE 开发一个名为 HRMZone 的人力资源管理系统(HRMS),其中一部分提供了访客简历的发布功能。该应用程序类似于求职网站,例如 http://www.jobpilot.com/。当我厌倦了编写相同的数据库代码时,我开始思考开发一个框架来最小化相同的努力;因为将业务实体持久化到后端服务器的代码看起来是一个通用模式。开发框架的动机来自于我对 Java 反射 API 的了解。
花了一整天的时间,我写出了我的第一个对象持久化框架(用 Java 编写);我最初将其命名为 AutoPersist Framework。有趣的是,它比我想象的更能节省我的精力。令人遗憾的是,几个月后,我看到一个稳定的持久化框架 'Hibernate' 已经发布了!看来我重新发明了同一个轮子!!是的,虽然是同一个轮子,但在一个方面它与 Hibernate 的形状不同;AutoPersist 不需要(现在仍然不需要)编写 ORM 映射文件(一种 XML 映射模式)。
在 Visual C# 发布后,我曾想用 C# 语言重写我的 AutoPersist
。我当时非常忙,未能实现重写。终于,本周,我在家中有了一些空闲时间,并用 C# 编写了 AutoPersist
的移植版本——这是第一个仅支持基本 CRUD 而不支持任何复杂或简单的关系完整性的版本。也就是说,AutoPersist
的第一个版本既不允许保存嵌套 object
,也不允许数据库实体之间存在主子关系。但有趣的是,它可以保存任何业务对象,而无需编写映射文件(XML 形式的模式映射)。
使用代码
AutoPersist API 的实际源代码可以在本文的源代码下载链接中找到;另一方面,演示下载包含该项目的演示。
假设我们有一个名为 Customer
的类,它定义了两个属性:Name
和 Address
。
public class Customer
{
private int id;
private string name;
private string address;
public Customer()
{
}
public int ID
{
set { this.id = value; }
get { return this.id; }
}
public string Name
{
set { this.name = value; }
get { return this.name; }
}
public string Address
{
set { this.address = value; }
get { return this.address; }
}
}
现在我们的目标是在不编写以数据库为中心的任何代码的情况下持久化一个 Customer
类型的 object
!我们将使用 PersistManager
,它是 AutoPersist API 的核心。要获得此类的实例,我们需要调用其 NewInstance()
方法,该方法接受一个 DBInfo
类型的参数来确定后端类型、连接字符串等。直到最近,DBInfo
仅提供连接字符串属性。因此,客户端的以下代码片段将返回 PersistManager
的一个实例:
DBInfo info = new DBInfo();
info.ConnectionString = // here is a connection string;
PersistManager objPersistManager = PersistManager.NewInstance(info);
在获得 PersistManager
类的实例后,我们可以通过以下方式保存我们的 Customer
:
Customer objCustomer = new Customer(); objPersistManager.CreateObject(objCustomer);
约定
要使用 AutoPersist API 保存业务对象,无需编写任何模式映射文件。那么 API 如何确定将创建或删除行的数据库表的名称呢?API 又将如何确定映射到对象属性名称的列的名称呢?
为了实现我们业务对象的自动持久化目标,我们必须遵循一些约定:
您的业务实体(您的类)必须遵循以下结构:
public class Customer
{
private int id;
// must have a no-arg default public constructor
public Customer()
{
}
// must have a ID property method of type int
public int ID
{
set { this.id = value; }
get { return this.id; }
}
}
在数据库表中:
- 必须有一个表,其名称必须与对象的名称匹配,即表名 =
object.GetType().Name
,其中 object 是您的对象。对于上面的示例,表名必须是 'Customer'。 - 必须有一个名为 'ID' 的自动递增列(MS Access 中的自动编号)。
- 必须有一些列,其名称必须与对象的所有属性名称匹配。例如,如果有一个名为
CustomerName
的属性,那么表中必须有一个名为 'CustomerName' 的列。
在演示项目中,'/database' 文件夹中有一个名为 'AutoPersist' 的 MS Access 数据库,用于演示持久化 Customer 实体的示例。
测试
在 AutoPersist API 的第一个版本中,仅在 MS Access 数据库上测试了类型为 'int'
和 'string'
的属性。为了访问数据库,我仅使用了 .NET Framework 的 OLE DB API。
增强功能
我们可以通过支持(部分或全部)以下内容来增强 Framework 代码以满足自己的需求:
- 保存嵌套对象
- 维护关系完整性
- 通过模式映射文件(通常是 XML 文件)实现自动和混合模式持久化
- 其他数据库服务器/应用程序
- 其他数据库访问 API,例如 ADO.NET
- 代码生成器,用于创建 ASP.NET 的 Web 窗体(包含代码隐藏和辅助类)或桌面应用程序的 WinForm。
以上所有都可以实现。如果您有任何问题或需要任何支持,请随时给我发电子邮件。
性能
在各个方面平衡一个程序几乎是不可能的。为了获得其他优势,我们需要在某个方面做出妥协。如果我们使用自动持久化,它大量依赖于利用反射 API,那么我们必须在应用程序的速度上做出妥协。反射需要在运行时(与设计时相反)内省对象结构,以找出要调用的方法/属性。这有点类似于 COM 中的后期绑定功能,这会造成性能瓶颈。
最终想法
显然,没有什么能阻止我们获得使用 AutoPersist 类 API 的优势。每个问题都必须有解决方案。实现性能优势很难,但并非不可能!您是否感到困惑?如果您感到困惑,请给我发电子邮件说明您的困惑 :-)