用少于 300 行代码实现持久对象管理






3.93/5 (14投票s)
介绍一种精简的方法,可以消除创建数据类中的大部分重复工作。
引言
在N层架构的数据访问层中,编写数据类涉及到大量重复性的工作。除了向相关的数据库表添加列之外,为类添加字段需要添加一个 private
变量来存储字段值,一个属性将它暴露给其他类,以及几行代码来确保该字段包含在插入、获取和更新操作中。如果你使用存储过程(对于CRUD查询来说非常愚蠢),还需要进行更多的修改。我相信您会同意,这是一项枯燥的工作。本文将介绍一种精简的方法,可以消除所有这些繁琐的工作,这样您只需添加一个getter/setter属性即可为数据类添加字段。
该技术涉及将SQL查询的编写和执行以及数据访问类中的变量存储抽象到一个基类中。在本示例的实现中,我将这个基类命名为 "PersistentDataObject
"。标题中提到的“少于300行代码”是指这个基类、它所使用的数据库访问类以及用于区分异常来源的几个异常类的代码行数。完整的代码包含在文章的最后。
背景
在我接触.NET之前,在一个单一的PHP项目中工作时,我就构思了这个想法。在编写了数百个基本相同的简单CRUD查询之后,我意识到可以轻松地将所有这些查询编写工作抽象出来,这样我就再也不用编写如此简单的查询了。这已经是过去一年多以前的事了。现在我是一名全职的ASP.NET开发人员,并且我认为这段代码已经达到了可以向世界展示的精炼程度。我在我构建的每个系统中都使用这里提供的代码,我非常喜欢它。也许您也会喜欢。
Using the Code
下面是一个简单的 Person
类,它通过继承 PersistentDataObject
创建
using System;
public class Person : PersistentDataObject
{
public Person() : base("Person", "PersonId") { }
public Person(int id) : this() { Load(id); }
public string FirstName
{
get { return Get<string>("FirstName"); }
set { Set("FirstName", value); }
}
public string LastName
{
get { return Get<string>("LastName"); }
set { Set("LastName", value); }
}
public DateTime DateOfBirth
{
get { return Get<DateTime>("DateOfBirth"); }
set { Set("DateOfBirth", value); }
}
public int? LuckyNumber
{
get { return Get<int?>("LuckyNumber"); }
set { Set("LuckyNumber", value); }
}
}
配置数据对象类
类所需的唯一配置值是数据检索的表名以及该表内的唯一ID列名(假定为整数列)。这些值通过构造函数传递给 PersistentDataObject
。
public Person() : base("Person", "PersonId") { }
从数据库获取数据
上面显示的 Person
类提供了一个接受整数标识符作为参数的构造函数。该构造函数委托给 PersistentDataObject
的 Load
方法,以获取数据库中具有指定标识符的行的所有数据。
public Person(int id) : this() { Load(id); }
提供了一个 Load
方法的重载,它接受一个 DataRow
作为参数。这允许使用已经从数据库获取的 DataRow
中的数据来填充对象。
将列值公开为属性
列值通过委托给 PersistentDataObject
的 Get
和 Set
方法来公开。 Get
方法接受一个 string
参数,指示从中检索值的列。同样, Set
方法接受一个 string
参数来指示要设置值的列。本示例显示了一个公开 DateTime
列的属性
public DateTime DateOfBirth
{
get { return Get<DateTime>("DateOfBirth"); }
set { Set("DateOfBirth", value); }
}
请注意, Get
方法使用泛型来指定应返回值的类型。指定的类型应与列的类型相匹配。
处理Null值
Nullable value types(可空值类型)用于处理允许 null
值的列。本示例显示了一个允许 null
值的整数列,它被公开为一个属性
public int? LuckyNumber
{
get { return Get<int?>("LuckyNumber"); }
set { Set("LuckyNumber", value); }
}
请注意,由于 string
是引用类型,因此不需要将其指定为可空类型即可接受 null
值。
插入、更新和删除
提供了一个 Save
方法,该方法同时处理在数据库中插入和更新对象。如果 Save
方法被调用在一个尚不存在于数据库中的对象上,则执行插入操作。如果对象已存在于数据库中,则执行更新操作。此代码片段显示了一个 Person
实例的创建及其在数据库中的记录插入
Person person = new Person();
person.FirstName = "Nick";
person.LastName = "Higgs";
person.DateOfBirth = new DateTime(1983, 2, 7);
person.Save();
提供了一个 Delete
方法,允许从数据库中删除对象。此代码显示了一个现有 Person 记录对象的实例化,然后从数据库中删除
Person person = new Person(51);
person.Delete();
闭幕词
以上就是全部解释。我发现这种技术不仅消除了编写数据对象的绝大多数重复工作,而且为数据访问层提供了坚实的基础,无论系统的复杂程度如何。
历史
修改自我的(现已停用)博客上的一篇文章
http://developmental-issues.blogspot.com/2007/04/simple-or-mapping-in-less-than-300.html