多层结构中自定义实体的使用

前言

程序设计中免不了要跟数据打交道, 在进行数据交互的过程中,我们可以采取很多.net自带的对象来帮助我们承载数据,如,datatable,dataset或者dataview,并把它们作为数据数据显示控件的数据原,通过数据邦定来显示数据..断开连接的数据使我们可以利用新的缓存技术,从而大大提高了应用程序的性能。这些类的功能使我们能够编写出更智能、更强大的函数,同时还能减少(有时候甚至是大大减少)常见活动所需的代码数量。

有些情况下非常适合使用 DataSet,例如在设计原型、开发小型系统和支持实用程序时。但是,在企业系统中使用 DataSet 可能并不是最佳的解决方案,因为对企业系统来说,易于维护要比投入市场的时间更重要。今天我们的目的就是探讨一种适合处理此类工作的 DataSet 的替代解决方案,即:自定义实体与集合。我们的首要任务是了解 DataSet 的缺点,以便理解我们要解决的问题。

DataSet 存在的问题

缺少抽象

寻找替代解决方案的第一个也是最明显的原因就是 DataSet 无法从数据库结构中提取代码。DataAdapter 可以很好地使您的代码独立于基础数据库供应商(Microsoft、Oracle、IBM 等),但不能抽象出数据库的核心组件:表、列和关系。这些核心数据库组件也是 DataSet 的核心组件。DataSet 和数据库不仅共享通用组件,不幸的是,它们还共享架构。假定有下面这样一个 Select 语句:

SELECT UserId, FirstName, LastName
FROM Users

我们知道这些值可以从 DataSet 中的 UserIdFirstNameLastName 这些 DataColumn 中获得。在aspx页面中借助数据邦定控件将数据显示给客户,然后不幸的是,如果果由于某种原因(为了性能而降级、为清楚起见而进行了标准化、要求发生了变化)导致数据库架构发生变化,变化就会一直影响 ASPX,即影响使用“FirstName”列名的 Databinder.Eval 行。这将立刻在您脑海中产生一个危险信号:数据库架构的变化会一直影响到 ASPX 代码吗?听起来不太像 N 层,对吗?

DataSet 无法提供适当抽象的另一个原因是它要求开发人员必须了解基础架构。我们所说的不是基础知识,而是关于列名称、类型和关系的所有知识。去掉这个要求不仅使您的代码不像我们看到的那样容易中断,还使代码更易于编写和维护。简单地说:

Convert.ToInt32(ds.Tables[0].Rows[i]["userId"]);

不仅难于阅读,而且需要非常熟悉列名称及其类型。理想情况下,您的业务层不需要知道有关基础数据库、数据库架构或 SQL 的任何内容。如果您像上述代码字符串中那样使用 DataSet(使用 CodeBehind 并不会有任何改善),您的业务层可能会很薄。

弱类型

DataSet 属于弱类型,因此容易出错,这意味着无论何时从 DataSet 中检索值,值都以 System.Object 的形式返回,您需要对这种值进行转换。您面临转换可能会失败的风险。不幸的是,失败不是在编译时发生,而是在运行时发生。另外,在处理弱类型的对象时,Microsoft Visual Studio.NET (VS.NET) 等工具对您的开发人员并没有太大的帮助。前面我们说过需要深入了解构架的知识,就是指这个意思。我们再来看一个非常常见的示例:

int userId = Convert.ToInt32(ds.Tables[0].Rows[0]("UserId"));

这段代码显示了从 DataSet 中检索值的可能方法——可能您的代码中到处都需要检索值

不幸的是,这些代码中的每一行都可能会产生大量的运行时错误:

1.

转换可能由于以下原因而失败:

值可能为空。

开发人员可能对基础数据类型判断有误(还是这个问题,即开发人员需要非常熟悉数据库架构)。

如果您使用序号值,谁知道位置 X 处实际上是一个什么样的列。

2.

ds.Tables(0) 可能返回一个空引用(如果 DAL 方法或存储过程中有任何部分失败)。

3.

“UserId”可能由于以下原因而是一个无效的列名称:

可能已经更改了名称。

可能不是由存储过程返回的。

可能包含错别字。

我们可以修改代码并以更安全的方式编写,即为 为转换添加 try/catch,但这些对开发人员都没有帮助。

“DataSet 是一个对象,对吗?但它并不是域对象,它不是一个‘苹果’或‘桔子’,而是一个‘DataSet’类型的对象。DataSet 是一只碗(它知道支持数据存储)。DataSet 是一个知道如何保存行和列的对象,它非常了解数据库。但是,我不希望返回碗,我希望返回域对象,例如‘苹果’。”1

DataSet 使数据之间保持一种关系,使它们更强大并且能够在关系数据库中方便地使用。不幸的是,这意味着您将失去 OO 的所有优点。 因为 DataSet 不能作为域对象,所以无法向它们添加功能。通常情况下,对象具有字段、属性和方法,它们的行为针对的是类的实例。

自定义实体类

DataSet 有关的大多数问题都可以利用 OO 编程的丰富功能在定义明确的业务层中解决。实际上,我们希望获得按照关系组织的数据(数据库),并将数据作为对象(代码)使用。这个概念就是,不是获得保存汽车信息的 DataTable,而是获得汽车对象(称为自定义实体或域对象)。

自定义实体是代表业务域的对象,因此,它们是业务层的基础。如果您有一个用户身份验证组件,您就可能具有 UserRole 对象。电子商务系统可能具有 SupplierMerchandise 对象,而房地产公司则可能具有 HouseRoomAddress 对象。在您的代码中,自定义实体只是一些类(实体和“类”之间具有非常密切的关系,就像在 OO 编程中使用的那样)。一个典型的 User 类可能如下所示:

public class User {
private int userId;
private string userName;
private string password;
public int UserId {
get { return userId; }
set { userId = value; }
  }
public string UserName {
get { return userName; }
set { userName = value; }
}
public string Password {
get { return password; }
set { password = value; }
}

public User() {}
public User(int id, string name, string password) {
this.UserId = id;
this.UserName = name;
this.Password = password;
}

为什么能够从它们获益?

使用自定义实体获得的主要好处来自这样一个简单的事实,即它们是完全受您控制的对象。具体而言,它们允许您:

利用继承和封装等 OO 技术。

添加自定义行为。

例如,我们的 User 类可以通过为其添加 UpdatePassword 函数而受益(我们可能会使用外部/实用程序函数对数据集执行此类操作,但会影响可读性/维护性)。另外,它们属于强类型,这表示我们可以获得 IntelliSense 支持:


1User 类的 IntelliSense

最后,因为自定义实体为强类型,所以不太需要进行容易出错的强制转换:

int userId = user.UserId
int userId = Convert.ToInt32(ds.Tables("users").Rows(0)("UserId"))

对象关系映射

正如前文所讨论的那样,此方法的主要挑战之一就是处理关系数据和对象之间的差异。因为我们的数据始终存储

在关系数据库中,所以我们只能在这两个世界之间架起一座桥梁。对于上文的 User 示例,我们可能希望在数

据库中建立一个如下所示的用户表:


2User 的数据视图

从这个关系架构映射到自定义实体是一个非常简单的事情:

public User GetUser(int userId) { SqlConnection connection = new SqlConnection(CONNECTION_STRING); SqlCommand command = new SqlCommand("GetUserById", connection); command.Parameters.Add("@UserId", SqlDbType.Int).Value = userId; SqlDataReader dr = null; try{ connection.Open(); dr = command.ExecuteReader(CommandBehavior.SingleRow); if (dr.Read()){ User user = new User(); user.UserId = Convert.ToInt32(dr["UserId"]); user.UserName = Convert.ToString(dr["UserName"]); user.Password = Convert.ToString(dr["Password"]); return user; } return null; }finally{ if (dr != null && !dr.IsClosed){ dr.Close(); } connection.Dispose(); command.Dispose(); } }

我们仍然按照通常的方式设置连接和命令对象,但接着创建了 User 类的一个新实例并从 DataReader 中填

充该实例。您仍然可以在此函数中使用 DataSet 并将其映射到您的自定义实体,但 DataSet 相对于

DataReader 的主要好处是前者提供了数据的断开连接的视图。在本例中,User 实例提供了断开连接的视图,

使我们可以利用 DataReader 的速度。

 

您现在了解什么是自定义实体和自定义实体的好处了吗,接下来我们将进一步探讨自定义集合的使用,以及

复杂系统中自定义实体的使用!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值