转至繁体中文版     | 网站首页 | 图文教程 | 资源下载 | 站长博客 | 图片素材 | 武汉seo | 武汉网站优化 | 
最新公告:     敏韬网|教学资源学习资料永久免费分享站!  [mintao  2008年9月2日]        
您现在的位置: 学习笔记 >> 图文教程 >> 站长学院 >> Web开发 >> 正文
掌握 ASP.NET 之路:自定义实体类简介         ★★★★

掌握 ASP.NET 之路:自定义实体类简介

作者:闵涛 文章来源:闵涛的学习笔记 点击数:2250 更新时间:2009/4/23 10:36:06
发布日期: 5/24/2005 | 更新日期: 5/24/2005

Karl Seguin
Microsoft Corporation

摘要:有些情况下,非类型化的 DataSet 可能并非数据操作的最佳解决方案。本指南的目的就是探讨 DataSet 的一种替代解决方案,即:自定义实体与集合。(本文包含一些指向英文站点的链接。)

引言

ADODB.RecordSet 和常常被遗忘的 MoveNext 的时代已经过去,取而代之的是 Microsoft ADO.NET 强大而又灵活的功能。我们的新武器就是 System.Data 名称空间,它的特点是具有速度极快的 DataReader 和功能丰富的 DataSet,而且打包在一个面向对象的强大模型中。能够使用这样的工具一点都不奇怪。任何 3 层体系结构都依靠可靠的数据访问层 (DAL) 将数据层与业务层完美地连接起来。高质量的 DAL 有助于改善代码的重新使用,它是获得高性能的关键,而且是完全透明的。

随着工具的改进,我们的开发模式也发生了变化。告别 MoveNext 并不只是让我们摆脱了繁琐的语法,它还让我们认识了断开连接的数据,这种数据对我们开发应用程序的方式产生了深刻的影响。

因为我们已经熟悉了 DataReader(其行为与 RecordSet 非常类似),所以没花多长时间就进一步开发出 DataAdapterDataSetDataTableDataView。正是在开发这些新对象的过程中不断得到磨炼的技能改变了我们的开发方式。断开连接的数据使我们可以利用新的缓存技术,从而大大提高了应用程序的性能。这些类的功能使我们能够编写出更智能、更强大的函数,同时还能减少(有时候甚至是大大减少)常见活动所需的代码数量。

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

记住,每种解决方案都有优缺点,所以 DataSet 的缺点可能比自定义实体的缺点(我们也将进行讨论)更容易让您接受。您和您的团队必须自己决定哪个解决方案更适合您的项目。记住要考虑解决方案的总成本,包括要求改变的实质所在以及生产后所需的时间比实际开发代码的时间更长的可能性。最后请注意,我所说的 DataSet 并不是类型化的 DataSet,但它确实可以弥补非类型化的 DataSet 的一些缺点。

DataSet 存在的问题

缺少抽象

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

SELECT UserId, FirstName, LastName
FROM Users

我们知道这些值可以从 DataSet 中的 UserIdFirstNameLastName 这些 DataColumn 中获得。

为什么会这么复杂?让我们看一个基本的日常示例。首先我们有一个简单的 DAL 函数:

''''Visual Basic .NET
Public Function GetAllUsers() As DataSet
Dim connection As New SqlConnection(CONNECTION_STRING)
Dim command As SqlCommand = New SqlCommand("GetUsers", connection)
command.CommandType = CommandType.StoredProcedure
Dim da As SqlDataAdapter = New SqlDataAdapter(command)
Try
Dim ds As DataSet = New DataSet
da.Fill(ds)
Return ds
Finally
connection.Dispose()
command.Dispose()
da.Dispose()
End Try
End Function

//C#
public DataSet GetAllUsers() {
SqlConnection connection = new SqlConnection(CONNECTION_STRING);
SqlCommand command = new SqlCommand("GetUsers", connection);
command.CommandType = CommandType.StoredProcedure;
SqlDataAdapter da = new SqlDataAdapter(command);
try {
DataSet ds = new DataSet();
da.Fill(ds);
return ds;
}finally {
connection.Dispose();
command.Dispose();
da.Dispose();
 }            
}

然后我们有一个页面,它使用重复器显示所有用户:

<HTML>
<body>
<form id="Form1" method="post" runat="server">
<asp:Repeater ID="users" Runat="server">
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem, "FirstName") %>
<br />
</ItemTemplate>
</asp:Repeater>
</form>
</body>
</HTML>
<script runat="server">
public sub page_load
users.DataSource = GetAllUsers()
users.DataBind()
end sub
</script>

正如我们所看到的那样,我们的 ASPX 页面利用 DAL 函数 GetAllUsers 作为重复器的 DataSource。如果由于某种原因(为了性能而降级、为清楚起见而进行了标准化、要求发生了变化)导致数据库架构发生变化,变化就会一直影响 ASPX,即影响使用“FirstName”列名的 Databinder.Eval 行。这将立刻在您脑海中产生一个危险信号:数据库架构的变化会一直影响到 ASPX 代码吗?听起来不太像 N 层,对吗?

如果我们所要做的只是对列进行简单的重命名,那么更改本例中的代码并不复杂。但是,如果在许多地方都使用了 GetAllUsers,更糟糕的是,如果将其作为为无数用户提供服务的 Web 服务,那又会怎么样呢?怎样才能轻松或安全地传播更改?对于这个基本示例而言,存储过程本身作为抽象层可能已经足够;但是依赖存储过程获得除最基本的保护以外的功能则可能会在以后造成更大的问题。可以将此视为一种硬编码;实质上,使用 DataSet 时,您可能需要在数据库架构(不管使用列名称还是序号位置)和应用层/业务层之间建立一个严格的连接。但愿以前的经验(或逻辑)已经让您了解到硬编码对维护工作以及将来的开发产生的影响。

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

Convert.ToInt32(ds.Tables[0].RowsIdea [I]["userId"]);

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

弱类型

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

DataSet 相反的情况

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

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

返回页首返回页首

自定义实体类

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

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

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

什么是自定义实体?

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

''''Visual Basic .NET
Public Class User
#Region "Fields and Properties"
Private _userId As Integer
Private _userName As String
Private _password As String
Public Property UserId() As Integer
Get
Return _userId
End Get
Set(ByVal Value As I

[1] [2] [3] [4]  下一页


[C语言系列]NET 中C#的switch语句的语法  [网页制作]需要掌握的八个CSS网页布局技巧!
[办公软件]掌握Excel十二条操作技巧,让工作更效益  [办公软件]喧宾不夺主—轻松掌握Word对象的叠放次序
[办公软件]掌握Word英文大小写转换,提高工作效益  [操作系统]全方位掌握系统自动更新的设置
[操作系统]掌握基本输入输出系统CMOS的使用及维护知识  [操作系统]轻松掌握10个Windows2003的配置技巧下篇
[操作系统]轻松掌握10个Windows2003的配置技巧上篇  [网络技术]让DHCP服务器不再是累赘就得掌握使用方法和技巧中…
教程录入:mintao    责任编辑:mintao 
  • 上一篇教程:

  • 下一篇教程:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
      注:本站部分文章源于互联网,版权归原作者所有!如有侵权,请原作者与本站联系,本站将立即删除! 本站文章除特别注明外均可转载,但需注明出处! [MinTao学以致用网]
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)

    同类栏目
    · Web开发  · 网页制作
    · 平面设计  · 网站运营
    · 网站推广  · 搜索优化
    · 建站心得  · 站长故事
    · 互联动态
    更多内容
    热门推荐 更多内容
  • 没有教程
  • 赞助链接
    更多内容
    闵涛博文 更多关于武汉SEO的内容
    500 - 内部服务器错误。

    500 - 内部服务器错误。

    您查找的资源存在问题,因而无法显示。

    | 设为首页 |加入收藏 | 联系站长 | 友情链接 | 版权申明 | 广告服务
    MinTao学以致用网

    Copyright @ 2007-2012 敏韬网(敏而好学,文韬武略--MinTao.Net)(学习笔记) Inc All Rights Reserved.
    闵涛 投放广告、内容合作请Q我! E_mail:admin@mintao.net(欢迎提供学习资源)

    站长:MinTao ICP备案号:鄂ICP备11006601号-18

    闵涛站盟:医药大全-武穴网A打造BCD……
    咸宁网络警察报警平台