科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道应用软件ASP.NET:自定义实体类简介(2)

ASP.NET:自定义实体类简介(2)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

自定义实体使您获得了面向对象的编程的丰富功能,并帮助您构建了可靠、可维护的 N 层体系结构的框架。

作者:microsoft 来源:Karl Seguin  2007年9月2日

关键字:

  • 评论
  • 分享微博
  • 分享邮件

弱类型

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

'Visual Basic.NET

Dim userId As Integer =

? Convert.ToInt32(ds.Tables(0).Rows(0)("UserId"))

Dim userId As Integer = CInt(ds.Tables(0).Rows(0)("UserId"))

Dim userId As Integer = CInt(ds.Tables(0).Rows(0)(0))

//C#

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

这段代码显示了从 DataSet 中检索值的可能方法——可能您的代码中到处都需要检索值(如果不进行转换,而您使用的又是 Visual Basic .NET,您可能会使用 Option Strict Off 这样的代码,而这会给您带来更大的麻烦。)

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

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

• 值可能为空。

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

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

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

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

• 可能已经更改了名称。

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

• 可能包含错别字。

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

更糟糕的是,正如我们前面所说,这不是抽象的。这意味着,每次要从 DataSet 中检索 userId 时,您都将面临上面提到的风险,或者需要对相同的保护性步骤进行重新编程(当然,实用程序功能可能会有助于降低风险)。弱类型对象将错误从设计时或编译时(这时总能够自动检测并轻松修复错误)转移到运行时(这时的错误可能会出现在生产过程中,而且更难查明)。

非面向对象

您不能仅仅因为 DataSet 是对象,而 C# 和 Visual Basic .NET 是面向对象 (OO) 的语言就能以面向对象的方式使用 DataSet。OO 编程的“hello world”是一个典型的 Person 类,该类又是 Employee 的子类。但 DataSet 并没有使此类继承或其他大多数 OO 技术成为可能(或者至少使它们变得自然/直观)。Scott Hanselman 是类实体的坚决支持者,他做出了最好的解释:

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

DataSet 使数据之间保持一种关系,使它们更强大并且能够在关系数据库中方便地使用。不幸的是,这意味着您将失去 OO 的所有优点。

因为 DataSet 不能作为域对象,所以无法向它们添加功能。通常情况下,对象具有字段、属性和方法,它们的行为针对的是类的实例。例如,您可能会将 Promote 或 CalcuateOvertimePay 函数与 User 对象相关联,该对象可以通过 someUser.Promote() 或 someUser.CalculateOverTimePay() 安全地调用。因为无法向 DataSet 添加方法,所以您需要使用实用程序功能来处理弱类型对象,并且在整个代码中包含硬编码值的更多实例。您一般会以过程代码结束,在过程代码中,您要么不断地从 DataSet 中获取数据,要么以繁琐的方式将它们存储在本地变量中并向其他位置传递。两种方法都有缺点,而且都没有任何优点。

与 DataSet 相反的情况

如果您认为数据访问层应返回 DataSet,您可能会漏掉一些重要的优点。其中一个原因是您可能正在使用一个较薄或不存在的业务层,除了其他问题外,它还限制了您进行抽象的能力。另外,因为您使用的是一般的预编译解决方案,所以很难利用 OO 技术。最后,Visual Studio.NET 等工具使开发人员无法轻松地利用弱类型对象(例如 DataSet),因此降低了效率并且增加了出错的可能性。

所有这些因素都以不同的方式对代码的可维护性产生了直接的影响。缺乏抽象使功能改善和错误修复变得更复杂、更危险。您无法充分利用 OO 提供的代码重新使用或可读性方面的改进。当然还有一点,无论您的开发人员处理的是业务逻辑还是表示逻辑,他们都必须非常了解您的基础数据结构。

自定义实体类

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

在了解自定义实体之前,让我们首先看一看我们将要面临的挑战。最明显的挑战就是所需代码的数量。我们不是简单地获取数据并自动填充 DataSet,而是获取数据并手动将数据映射到自定义实体(必须先创建好)。由于这是一项重复性的任务,我们可以使用代码生成工具或 O/R 映射器(后文有详细的介绍)来减轻工作量。更大的问题是将数据从关系世界映射到对象世界的具体过程。对于简单的系统,映射通常是直接的,但是随着复杂性的增加,这两个世界之间的差异就会产生问题。例如,继承在对象世界中是获得代码重新使用以及可维护性的重要技术。不幸的是,继承对关系数据库来说却是一个陌生的概念。另外一个例子就是处理关系的方式不同:对象世界依靠维护单个对象的引用,而关系世界则是利用外键。

因为代码的数量以及关系数据和对象之间的差异不断增加,看起来这个方法并不太适合更复杂的系统,但事实正好相反。通过将各种问题隔离到一个层中,即映射过程(同样可以自动化),复杂的系统也可以从此方法获益。另外,此方法已经很常用,这意味着可以通过几种已有的设计模式彻底解决增加的复杂性。前面讨论的 DataSet 的缺点在复杂系统中将成倍扩大,最后您会得出这样一个系统,它欠缺灵活应变能力的缺点恰好超出其构建的难度。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章