科技行者

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

知识库

知识库 安全导航

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

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

  • 扫一扫
    分享文章到微信

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

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

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

关键字:

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

高级内容

本指南的目的是介绍自定义实体与集合的概念及使用。使用自定义实体是业界广泛采用的做法,因此,也就产生了同样多的模式以处理各种情况。设计模式具有优势的原因有很多。首先,在处理具体的情况时,您可能不是第一次碰到某个给定的问题。设计模式使您可以重新使用给定问题的已经过尝试和测试的解决方案(虽然设计模式并不意味着全盘照抄,但它们几乎总是能够为解决方案提供一个可靠的基础)。相应地,这使您对系统随着复杂性增加而进行缩放的能力充满了信心,不仅因为它是一个广泛使用的方法,还因为它具有详尽的记录。设计模式还为您提供了一个通用的词汇表,使知识的传播和传授更容易实现。

不能说设计模式只适用于自定义实体,实际上许多设计模式都并非如此。但是,如果您找机会试一下,您可能会惊喜地发现许多记载详尽的模式确实适用于自定义实体和映射过程。

最后这一部分专门介绍大型或较复杂的系统可能会碰到的一些高级情况。因为大多数主题都可能值得您单独学习,所以我会尽量为您提供一些入门资料。

Martin Fowler 的 Patterns of Enterprise Application Architecture 就是一个很好的入门材料,它不仅可以作为常见设计模式的优秀参考(具有详细的解释和大量的示例代码),而且它的前 100 页确实可以让您透彻地了解整个概念。另外,Fowler 还提供了一个联机模式目录,它对于已经熟悉概念但需要一个便利参考的人士很有用。

并发

前面的示例介绍的都是从数据库中提取数据并根据这些数据创建对象。总体而言,更新、删除和插入数据等操作是很直观的。我们的业务层负责创建对象、将对象传递给数据访问层,然后让数据访问层处理对象世界与关系世界之间的映射。例如:

'Visual Basic .NET

Public sub UpdateUser(ByVal user As User)

Dim connection As New SqlConnection(CONNECTION_STRING)

Dim command As New SqlCommand("UpdateUser", connection)

' 可以借助可重新使用的函数对此进行反向映射

command.Parameters.Add("@UserId", SqlDbType.Int)

command.Parameters(0).Value = user.UserId

command.Parameters.Add("@Password", SqlDbType.VarChar, 64)

command.Parameters(1).Value = user.Password

command.Parameters.Add("@UserName", SqlDbType.VarChar, 128)

command.Parameters(2).Value = user.UserName

Try

connection.Open()

command.ExecuteNonQuery()

Finally

connection.Dispose()

command.Dispose()

End Try

End Sub

//C#

public void UpdateUser(User user) {

SqlConnection connection = new SqlConnection(CONNECTION_STRING);

SqlCommand command = new SqlCommand("UpdateUser", connection);

// 可以借助可重新使用的函数对此进行反向映射

command.Parameters.Add("@UserId", SqlDbType.Int);

command.Parameters[0].Value = user.UserId;

command.Parameters.Add("@Password", SqlDbType.VarChar, 64);

command.Parameters[1].Value = user.Password;

command.Parameters.Add("@UserName", SqlDbType.VarChar, 128);

command.Parameters[2].Value = user.UserName;

try{

connection.Open();

command.ExecuteNonQuery();

}finally{

connection.Dispose();

command.Dispose();

}

}

但在处理并发时就不那么直观了,也就是说,当两个用户试图同时更新相同的数据时会出现什么情况呢?默认的行为(如果您没有执行任何操作)是最后提交数据的人将覆盖以前所有的工作。这可能不是理想的情况,因为一个用户的工作将在未获得任何提示的情况下被覆盖。要完全避免所有冲突,一种方法就是使用消极的并发技术;但此方法需要具有某种锁定机制,这可能很难通过可缩放的方式实现。替代方法就是使用积极的并发技术。让第一个提交的用户控制并通知后面的用户是通常采取的更温和、更用户友好的方法。这可以通过某种行版本控制(例如时间戳)来实现。

参考资料

• Introduction to Data Concurrency in ADO.NET

• CSLA.NET's concurrency techniques

• Unit of Work design pattern

• Optimistic offline lock design pattern

• Pessimistic offline lock design pattern

性能



与合理的灵活性和功能问题相对的是,我们经常担心细小的性能差异。尽管性能的确很重要,但提供适用于一切情况而不是最简单情况的通用原则通常很难。例如,将自定义集合与 DataSet 相比,哪个更快?使用自定义集合,您可以大量使用 DataReader,这是从数据库中提取数据的较快方式。但答案实际上取决于您使用它们的方式以及处理的数据类型,所以一般性的说明没有任何用。更重要的一点是要认识到,不管您能节省多少处理时间,与维护性方面的差异相比都可能微不足道。

当然,并不是说您不可能找到一个既具有高性能又可维护的解决方案。虽然我强调说答案实际上取决于您的使用方式,但的确有一些模式可以帮助您最大程度地提高性能。但是,首先要知道的是自定义实体与集合缓存以及 DataSet,并且能够利用相同的机制(类似于 HttpCache)。DataSet 的优势之一是它能够编写 Select 语句,以便只获取所需的信息。使用自定义实体时,您常常感到不得不填充整个实体以及子实体。例如,如果要通过 DataSet 显示一个 Organization 列表,您可以只提取 OganizationId、Name 和 Address 并将其绑定到重复器。使用自定义实体时,我总觉得还需要获取所有其他的 Organization 信息,如果该组织通过了 ISO 认证,则可能是一个位标记,即所有员工、其他联系信息等的集合。可能其他人没有碰到这个大难题,但幸运的是,如果我们愿意,我们可以对自定义实体进行很好的控制。最常用的方法是使用一种延迟加载模式,它只在首次需要时获取信息(可以很好地封装在属性中)。这种对各个属性的控制提供了通过其他方式无法轻易获得的巨大灵活性(请想象一下在 DataColumn 级别执行类似操作的情况)。

参考资料

• Lazy Load 设计模式

• CSLA.NET lazy load

排序与筛选

虽然 DataView 对排序和筛选的内置支持需要您了解有关 SQL 和基础数据结构的知识,但它提供的方便确实是自定义集合所不具备的。我们仍然可以排序和筛选,但首先需要编写功能。因为技术不一定是最先进的,所以代码的完整描述不属于本节要讨论的范围。大多数技术都很相似,例如使用筛选器类筛选集合以及使用比较器类进行排序,我认为不存在固定的模式。但是,的确存在一些参考资料:

• Generic sort function

• Sorting & Filtering Custom Collections 教程

代码生成



解决概念上的障碍后,自定义实体与集合的主要缺点就是灵活性、抽象和维护性差所导致的代码数量的增加。实际上,您可能会认为我所说的维护成本和错误的降低这一切都抵不上代码的增加。虽然这一观点是成立的(同样,因为任何解决方案都不是完美无缺的),但可以通过设计模式和框架(例如 CSLA.NET)大大缓解此问题。代码生成工具与模式和框架完全不同,这些工具可以大大降低您实际需要编写的代码数量。本指南最初打算专门辟出一节详细介绍代码生成工具,特别是流行的免费 CodeSmith;但现有的许多参考资料都可能超出了我自己对该产品的认识。

在继续之前,我认识到代码生成听起来像天方夜谭一样。但经过正确的使用和理解后,它的确是您工具包中不可缺少的一个强大的武器,即使您没有处理自定义实体也是如此。虽然代码生成的确不仅仅适用于自定义实体,但很多都是专为自定义实体而设计的。原因很简单:自定义实体需要大量重复代码。

简言之,代码生成是如何工作的?构想听起来好像遥不可及甚至反而会降低效率,但您基本上通过编写代码(模板)来生成代码。例如,CodeSmith 附带了许多强大的类,使您可以连接到数据库并获取所有属性:表、列(类型、大小等)和关系。获得这些信息后,我们前面讨论的大部分工作都可以自动完成。例如,开发人员可以选择一个表,然后使用正确的模板自动创建自定义实体(带有正确的字段、属性和构造函数),并获得映射函数、自定义集合以及基本的选择、插入、更新和删除功能。甚至还可以更进一步,实现排序、筛选以及我们提到的其他高级功能。

CodeSmith 还附带了许多现成的模板,可以作为很好的学习资料。最后,CodeSmith 还为实现 CSLA.NET 框架提供了许多模板。我最初只花了几个小时来学习基本概念、熟悉 CodeSmith 的功能,但它为我节省的时间已经多得无法计算了。另外,如果所有的开发人员都使用相同的模板,代码的高度一致性将使您能够轻松地继续其他人的工作。

参考资料

• Code Generation with CodeSmith

• CodeSmith 主页

O/R 映射器

即使因为对 O/R 映射器知之甚少使我不敢随便对它们发表议论,但它们自身的潜在价值使其不容忽视。代码生成器生成基于模板的代码,供您复制并粘贴到您自己的源代码中,而 O/R 映射器则在运行时通过某种配置机制动态生成代码。例如,在 XML 文件中,您可以指定某个表的列 X 映射到某个实体的属性 Y。您仍然需要创建自定义实体,但是集合、映射和其他数据访问函数(包括存储过程)都是动态创建的。从理论上讲,O/R 映射器几乎可以完全解决自定义实体存在的问题。随着关系世界和对象世界的差异越来越明显以及映射过程越来越复杂,O/R 映射器的价值就变得越发不可限量了。O/R 映射器的两个缺点据说就是不够安全和性能较差(至少在 .NET 环境中是这样)。根据我所阅读的资料,我确信它们并不是不够安全,虽然在有些情况下性能较差,但在另外一些情况下却表现突出。O/R 映射器并不适合所有情况,但如果您要处理复杂的系统,则应尝试一下它们的功能。

参考资料

• Mapper 设计模式

• Data Mapper 设计模式

• Wilson ORMapper

• Frans Bouma 关于 O/R 映射的帖子

• LLBGenPro

• NHibernate

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

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

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