当前位置: 首页 > 编程日记 > 正文

对比.Net PetShop和Duwamish来探讨Ado.Net的数据库编程模式

作者:卢彦
.NET PetShop和Duwamish简单介绍
相信大家一定听说过有名的"宠物店大战",没错,本文的主角之一就是获胜方.NET PetShop,微软号称以27倍的速度和1/4的代码量遥遥领先于基于J2EE的PetStore宠物商店。虽然SUN也曾对此抱怨过不满,指责此"大战"有水分,不过无论如何,.NET PetShop绝对是一个经典的.NET实例教程,至少为我们提供了一条赶超J2EE的“捷径” :),它的下载地址是:http://www.gotdotnet.com/team/compare

.NET PetShop宠物网上商店首页


而Duwamish则是一个外表简单,内部却极其复杂的一个网上书店的.NET完整应用范例,作为一个微软官方的Sample,它同时提供了C#和VB.NET两种语言版本,并且还附上了大量详尽的中文资料,如果打印出来,实在是居家旅行,临睡入厕必备之物。什么?您没听说过?呵呵,如果您装了Visual Studio .NET的话,它就在您的硬盘上静静的躺着呢,不过还没有被安装,您可以在您的VS.NET 的Enterprise Samples目录下找到并安装它,例如:C:/Program Files/Microsoft Visual Studio .NET/Enterprise Samples/Duwamish 7.0 CS。
Duwamish网上电子书店首页


结构简述
两家商店都采用了n层应用结构(毫无疑问,n层结构的应用架构应该绝对是您开发.NET应用的首选,哪怕您只想做一个网页计数器),不同的是,PetShop采用的是最常见的三层应用结构,分别为表示层,中间层和数据层。而Duwamish则采用的是一个四层应用结构,并使用不同的项目分隔开,分别为表示层,业务外观层,业务规则层和数据层。至于这两种结构分别有什么优点和缺点,以及为什么要这么分层,我们不进行详细讨论,因为本文的重点不在于此。我们主要分析的是他们的数据库编程的模式。
Duwamish数据访问剖析
首先,我们来看看Duwamish书店,它采用的是DataAdapter和DataSet配合的数据存储模式,所不同的是,它对DataSet进行子类化扩展作为数据载体,也就是采用定制的DataSet来进行层间的数据传输,下面是一个定制的DataSet示例:
public class BookData : DataSet
{
public BookData()
{
//
// Create the tables in the dataset
//
BuildDataTables();
}
private void BuildDataTables()
{
//
// Create the Books table
//
DataTable table   = new DataTable(BOOKS_TABLE);
DataColumnCollection columns = table.Columns;
columns.Add(PKID_FIELD, typeof(System.Int32));
columns.Add(TYPE_ID_FIELD, typeof(System.Int32));
columns.Add(PUBLISHER_ID_FIELD, typeof(System.Int32));
columns.Add(PUBLICATION_YEAR_FIELD, typeof(System.Int16));
columns.Add(ISBN_FIELD, typeof(System.String));
columns.Add(IMAGE_FILE_SPEC_FIELD, typeof(System.String));
columns.Add(TITLE_FIELD, typeof(System.String));
columns.Add(DESCRIPTION_FIELD, typeof(System.String));
columns.Add(UNIT_PRICE_FIELD, typeof(System.Decimal));
columns.Add(UNIT_COST_FIELD, typeof(System.Decimal));
columns.Add(ITEM_TYPE_FIELD, typeof(System.String));
columns.Add(PUBLISHER_NAME_FIELD, typeof(System.String));
this.Tables.Add(table);
}
………
}
我们可以看到它有一个BuildDataTables方法,并且在构造函数中调用,这样,定制的Books表就和这个DataSet捆绑在一起了,省得以后还要进行Column Mapping,这真是个好主意,我怎么就没有想到呢? :)
解决了数据结构,接下来看看数据层的代码实现,在Duwamish中,数据层中有5个类,分别是Books,Categories,Customers和Orders,每个类分别只负责有关数据的存取。下面是其中一个类的示例代码:
private SqlDataAdapter dsCommand;
public BookData GetBookById(int bookId)
{
return FillBookData("GetBookById", "@BookId", bookId.ToString());
}
private BookData FillBookData(String commandText, String paramName, String paramValue)
{
if (dsCommand == null )
{
throw new System.ObjectDisposedException( GetType().FullName );
}            
BookData   data    = new BookData();
SqlCommand command = dsCommand.SelectCommand;
command.CommandText = commandText;
command.CommandType = CommandType.StoredProcedure; // use stored proc for perf
SqlParameter param = new SqlParameter(paramName, SqlDbType.NVarChar, 255);
param.Value = paramValue;
command.Parameters.Add(param);            
dsCommand.Fill(data);
return data;
}
这里就是数据层的代码了,我们在这里可以看到Duwamish采用了DataAdapter来将数据填充到定制的DataSet中,然后返回该DataSet。我感到很奇怪的是在数据存取层中竟然可以看到GetBookById这样具体的数据存取方法,虽然最后还是有一个抽象出来的FillBookData方法,但是上面还有三层啊,底层都做到这份上了,那上层都做些什么呢?答案是数据检查,上层基本上都在做一些很严密的数据合法性校验(当然也会包括一些比较复杂的事务逻辑,但是并不多),示例代码如下:
public CustomerData GetCustomerByEmail(String emailAddress, String password)
{
//
// Check preconditions
//
ApplicationAssert.CheckCondition(emailAddress != String.Empty, "Email address is required",
ApplicationAssert.LineNumber);
ApplicationAssert.CheckCondition(password != String.Empty, "Password is required", 
ApplicationAssert.LineNumber);
//
// Get the customer dataSet
//
CustomerData dataSet;
using (DataAccess.Customers customersDataAccess = new DataAccess.Customers())
{
dataSet = customersDataAccess.LoadCustomerByEmail(emailAddress);
}
//    
// Verify the customer's password
//
DataRowCollection rows = dataSet.Tables[CustomerData.CUSTOMERS_TABLE].Rows;
if ( ( rows.Count == 1 ) && rows[0][CustomerData.PASSWORD_FIELD].Equals(password) )
{
return dataSet;
}
else
{        
return null;
}
} 
在这个方法中,真正进行数据存取的实际上只有
dataSet = customersDataAccess.LoadCustomerByEmail(emailAddress);
这么一句,是直接调用的数据层。其它都是在进行合法性校验,我们可以感悟到,进行一个真正的企业级开发需要考虑的系统健壮性有多么重要。
.NET PetShop数据访问剖析
OK,Duwamish看完了,下面我们来看看PetShop的数据访问机制。
PetShop只有一个项目,它采用的分层办法是将中间层和数据层都写成cs文件放在Components目录里,其中数据层就是一个名为Database的类,它封装了所有对数据库的底层操作。下面是示例代码段:
public void RunProc(string procName, out SqlDataReader dataReader) 
{
SqlCommand cmd = CreateCommand(procName, null);
dataReader = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
}
我们看到了一个跟Duwamish截然不同的另一种数据访问方式,它将所有的数据访问方法抽象出来做成一个RunProc方法,至于返回数据呢,呵呵,它有点偷懒,直接返回一个DataReader给你,你自己去读吧。还记得Duwamish采用的层间数据传输载体是什么吗?对了,是DataSet,它被数据层填充后返回给了中间层。但是这里,数据层和传输层的数据传输载体变成了DataReader,实际上,还不能称它为数据载体,因为数据还没开始读呢,在这里,DataReader的作用和指针有点类似,也许我们应该称它为“数据引用”:)
接着往下看,DataReader被怎么“处理”的:
public ProductResults[] GetList(string catid, int currentPage, int pageSize, ref int numResults) 
{
numResults = 0;
int index=0;
SqlDataReader reader = GetList(catid);
ProductResults[] results = new ProductResults[pageSize];
// now loop through the list and pull out items of the specified page
int start = (int)((currentPage - 1) * pageSize);
if (start <= 0) start = 1;
// skip 
for (int i = 0; i < start - 1; i++) {
if (reader.Read()) numResults++;
}
if (start > 1) reader.Read();
// read the data we are interested in
while (reader.Read()) {
if (index < pageSize) {
results[index] = new ProductResults();
results[index].productid = reader.GetString(0);
results[index].name =  reader.GetString(1);
index++;
}
numResults++;   
}
reader.Close();
// see if need to redim array
if (index == pageSize)
return results;
else {
// not a full page, redim array
ProductResults[] results2 = new ProductResults[index];
Array.Copy(results, results2, index);
return results2;
}    
}
注意到currentPage和pageSize了吗?原来在这里就进行了数据分页,只返回满足需要的最少的数据量,而不是象我们很多喜欢偷懒的人一样,简单的将整个DataTable一股脑的绑定到DataGrid,造成大量的数据冗余。
在这里,数据被真正的读出来,并且被手动填充到一个自定义的对象数组中,我们来看看这个数组的定义:
public class ProductResults 
{
private string m_productid;
private string m_name;
// product props
public string productid {
get { return m_productid; }
set { m_productid = value; }  
}
public string name {
get { return m_name; }
set { m_name = value; }  
}
}
非常之简单,不过我有点奇怪为什么不使用struct呢?是不是.NET中struct和class的性能差距已经可以忽略不计了?
分析总结
通过观察这两个商店的具体实现,我们得到了两个不同的数据访问模式,Duwamish采用的是以DataSet为核心,因为DataSet提供了这方面大量的相关方法,所以整个应用的数据传输,数据格式定义,数据校验都围绕着DataSet来进行,整个架构定义非常清晰和严谨,但是却显得有些庞大。PetShop在整个程序中没有采用一个DataSet,程序非常的简洁,轻灵,但是没有Duwamish那么强的健壮性。这两个程序是Microsoft公司不同的小组写出来的代码,所以有着不同风格。不过都应该能代表.NET的标准模式。看到这里,你应该对文章开头提出的那些疑问有一个比较形象的认识了吧。
另外,请再次注意,PetShop在打开数据连接之后,并没有马上读取数据,而是将DataReader传递给另外的对象来执行数据读的操作,然后才关闭连接。这样,数据连接的时间加长了,而数据库连接是一项非常宝贵的服务器资源,相比之下,Dawamish在连接数据库之后马上进行填充,然后迅速释放掉数据库连接的方式更加有利于大量用户的并发访问。
再一点,上文的程序中没有提到更新操作,PetShop采用的是使用Command对象执行单个存储过程的方式来进行更新操作,是属于一种在线即时数据更新模式。而Dawamish采用的是DataAdapter的Update方法,将DataSet的改变一次性的提交到数据库中,属于离线数据更新模式。这种模式的好处是可以一次性更新大批量数据,减少数据库的连接次数。缺点是如果数据库在改动非常频繁的情况下需要实时的跟踪数据变化就不合适了。需要根据具体的情况采用具体的数据更新办法。
总的来说,如果您只需要快速的读取数据并显示出来,推荐您采用DataReader,如果您需要对数据进行大量的修改,还有大量并发访问的可能,而且不需要实时的跟踪数据库的变化,推荐您使用DataSet。当然,这两种情况有点极端了,实际的应用环境也许有着很复杂的条件,具体需要您自己审时度势,综合采用,不过我个人还是比较喜欢PetShop那种轻灵的风格 :)
本文只尝试对以上两个典型的.NET应用例程的数据访问机制做了一个简单的追踪分析,如果有希望能对此例进行其它方面的研究或者就本话题继续进行更加深入探讨的朋友,请发Email到我的信箱:nluyan@163.NET,谢谢。

相关文章:

如何直接将HTML代码加载到TWebBrowser

wbRecvContent//为 webbrowser控件 procedure TFrmMain.ShowHtmlCentent(slt: TStrings); var aMemory: TMemoryStream; pbuf: PAnsiChar; begin aMemory : TMemoryStream.Create(); try aMemory.Clear; slt.SaveToStream(aMemory); aMemory.Seek(0, soBeginning); wbRecvConte…

JavaScript基础(一) 数据类型

动态类型 JavaScript 是一种弱类型或者说动态语言。这意味着你不用提前声明变量的类型&#xff0c;在程序运行过程中&#xff0c;类型会被自动确定。 数据类型 最新的 ECMAScript 标准定义了 7 种数据类型: 6 种 原始类型: BooleanNullUndefinedNumberStringSymbol (ECMAScript…

DataGrid入门经典(C#)

作者&#xff1a;未知 请作者速与本人联系这篇文章主要介绍如何在DataGrid控件中实现编辑、删除、分类以及分页操作。为了实现我们的意图&#xff0c;我们使用SqlServer2000自带的NorthWind数据库。程序分为两部分&#xff1a; 1.包含HTML代码的.ASPX文件 2.包含所有逻辑及…

(札记)Altera Stratix IV系列FPGA TRUE LVDS RX input termination 在Quartus工程中的设置方法...

Altera Stratix IV系列FPGA Row bank的TRUE LVDS_RX支持oct&#xff08;on chip termination),所以设计的时候不需要外接一个100ohm电阻。备注&#xff1a;我使用的是友晶科技&#xff08;Terasic&#xff09;的DE4。 所以当我们使用到Stratix iv系列FPGA的row bank的lvds的时候…

直播:AI时代,普通程序员该如何转人工智能(限免报名)

常常有小伙伴在后台反馈&#xff1a;想了解人工智能&#xff0c;但是该怎么学&#xff1f;自学难度大又没有效果&#xff0c;该怎么办&#xff1f;CSDN为了解决这个难题&#xff0c;联合唐宇迪老师为大家带来了一场精彩的直播【年薪百万AI工程师亲授&#xff1a;小白实战培养计…

沃通免费SSL证书申请指南

我们在做一些exchange或lync项目的时候很多时候都会用到公网证书&#xff0c;比如&#xff1a;我们做exchange2013和Office 365混合部署&#xff0c;或者通过SEM暂存迁移或CEM直接转换迁移的时候都需要用到公网证书,下面为大家介绍1个免费的SSL证书及申请的方法&#xff0c;希望…

使用VS.NET2003编写存储过程

作者&#xff1a;未知 请作者速与本人联系数据表定义了如何在数据库中存储数据&#xff0c;但没有说明如何存取数据。我们还需要了解读写记录以便从表中再次调用选定行和列的详细信息。开发人员通常会在其代码中编写一些特殊的查询语句&#xff0c;用于读写数据。这不仅会导致…

谈Linux的安全设置

如今系统的安全变的越来越重要了&#xff0c;这里我想把我平时比较常使用的一些linux下的基本的安全措施写出来和大家探讨一下&#xff0c;让我们的Linux系统变得可靠。 1、BIOS的安全设置 这是最基本的了&#xff0c;也是最简单的了。一定要给你的BIOS设置密码&#…

亮风台提出用完全可训练的图匹配方法,优于最新SOTA | CVPR 2020

出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09; 6月14日-19日&#xff0c;CVPR 2020在线上举行&#xff0c;据了解&#xff0c;本届大会共收到6656篇投稿&#xff0c;接收论文1470篇&#xff0c;录用率约22%&#xff0c;低于ICCV 2019论文录用率&#xff08;25%&…

数组与纠结的排序篇

数组之纠结的排序 1.数组是什么&#xff1f; 数组&#xff1a;所谓数组&#xff0c;就是相同数据类型的元素按一定顺序排列的集合&#xff0c;就是把有限个类型相同的变量用一个名字命名&#xff0c;然后用编号区分他们的变量的集合&#xff0c;这个名字称为数组名&#xff0c;…

ASP.NET结合COM组件发送Email

在开发电子邮件发送程序的时候&#xff0c;我们经常需要使用到相应的组件&#xff0c;其实不需要第三方的组件&#xff08;例如&#xff1a;Jmail&#xff09;照常可以做到发送Email的功能。 在系统目录&#xff08;如c:/winnt或c:/windows&#xff09;的system32子目录中可以找…

卡巴循环30天不限次数循环试用工具

本文需评论之后方可查看&#xff01; echo off title 卡巴循环30天不限次数循环试用工具 echo off echo 卡巴循环30天不限次数循环试用工具 echo. echo echo 卡巴斯基6/7/8试用过期时清除系统中使用痕迹&#xff0c;就象新系统重新安装卡巴一样 echo 1、在屏幕右下角红V卡巴图…

微软CEO纳德拉对话沈向洋:那些未来可期的计算机视觉研究与应用

编者按&#xff1a;6月16日&#xff0c;CVPR 2020 大会以全球连线的形式如期开幕。在大会的首场主题演讲中&#xff0c;微软公司 CEO 萨提亚纳德拉与微软公司前执行副总裁沈向洋进行了一场精彩的炉边对谈&#xff0c;分享了对计算机视觉、人工智能研究与应用前景的思考与展望。…

SoapUI进行REST请求,POST方法提交到数据库的数据乱码问题

一开始以为要把json字符串的key和value一个一个的加进去&#xff0c;结果总是报 300&#xff0c;参数错误&#xff0c;后来才发现&#xff08;https://www.joecolantonio.com/2013/08/31/soapui-how-to-post-json-to-a-rest-service-2/&#xff09;可以直接在下面粘贴就好了&am…

网站信息统计的简单实现过程

作者&#xff1a; pcskySQL语句如下&#xff1a; SELECT DD.SumHits, AA.CountArt, CC.WeekUpdate, BB.RegUserNumFROM(SELECT COUNT(newsid) AS CountArt FROM article) AA,(SELECT COUNT(id) AS RegUserNum FROM Admin) BB,(SELECT COUNT(newsid) AS WeekUpdate FROM(articl…

谈谈C#中类成员的执行顺序.

今天我们来谈谈C#中子类和父类中静态成员以及构造函数的执行顺序&#xff0c;这个地方向来是初学C#的人比较迷惑的地方&#xff0c;也是各大公司最喜欢拿来出面试题的地方。 下面我们分情况来分析。 1. 普通构造函数和静态构造函数的执行顺序。 对于单个的类&#xff0c;它的静…

AI 重塑 IT的 5 种方式

作者 | Stephanie Overby译者 | 火火酱&#xff0c;责编 | Carol封图 | CSDN 下载于视觉中国Gartner最新的人工智能&#xff08;AI&#xff09;hype cycle报告指出&#xff0c;AI在未来五年中CIO议程中的排名十分靠前&#xff0c;对潜在业务转型具有重要影响。然而&#xff0c;…

[原创]Gerrit中文乱码问题解决方案分享

应开发同事的要求,部署了GitlabGerritJenkins的持续集成环境. 但是发现了一个问题,Gerrit登陆后有中文乱码出现. 具体情况如下: (1)Git代码中的中文乱码处理: 为妥善解决中文编码的问题&#xff0c;对所有git repository做如下约定&#xff1a;所有文本文件都必须存储成utf8编码…

UVA 10700 Camel trading

UVA_10700我们可以猜到最大值一定是先算和后算积&#xff0c;最小值一定是先算积后算和,因为a*bc<a*(bc)。此外&#xff0c;这个题目数据有可能比较大&#xff0c;所以要采用long long int或者double来处理数据。 #include<stdio.h>#include<string.h>#include&…

在ASP.NET中操作文件的例子

利用SYSTEM.IO 名空间中的STREAMWRITER,STREAMREADER及FILE类,完成文件读、写、删除的操作。 -------------------------------------------------------------------------------- 1、写文件 writefile.aspx <% Import Namespace"System.IO" %> 引…

云原生如此重要,可惜80%的人都不知道

文 | Aholiab责编 | Carol封图 | CSDN 下载自视觉中国2020年&#xff0c;一场疫情给中国企业带来了一次「被动数字化升级」&#xff0c;很多企业第一次认识到了信息化的重要性。今天&#xff0c;数字经济已无处不在&#xff0c;根据中国信息化百人会的研究报告显示&#xff0c;…

CentOS 7 yum 安装 MySQL5.7

为什么80%的码农都做不了架构师&#xff1f;>>> 0、环境 本文操作系统: CentOS 7.2.1511 x86_64 MySQL 版本: 5.7.13 1、下载 MySQL 官方的 Yum Repository 从 MySQL 官网选取合适的 MySQL 版本&#xff0c;获取下载地址。 然后使用 wget 下载&#xff1a; [rootce…

万字长文带你入门 GCN

来源 | 阿泽的学习笔记&#xff08;ID: aze_learning&#xff09;Convolutional Neural NetworkCNN 在图像识别等任务中具有重要作用&#xff0c;主要是因为 CNN 利用了图片在其域中的平移不变性。由于图结构不存在平移不变性&#xff0c;所以 CNN 无法直接在图上进行卷积。1.1…

VS.Net中程序集的Debug版本和Release版本的区别

作者&#xff1a;未知 请作者速与本人联系前几天看到豆腐的文章介绍如何知道程序集是Debug版还是Release版&#xff0c;之前只知道某些软件从功能上有企业版、标准版之分&#xff0c;却从不知道.Net程序集还有Debug和Release之区别&#xff0c;真是惭愧学了这一年C#。然后在博…

《CLR via C#》笔记——CLR的执行模型

一&#xff0e;将源代码编译成托管代码1&#xff0c; CLR&#xff08;Common Language Runtime&#xff09;公共语言运行时是一个可由多种语言使用的“运行时”&#xff0c;CLR的核心功能&#xff08;比如内存管理&#xff0c;程序集加载&#xff0c;安全性&#xff0c;异常处理…

telnet时显示:允许更多到 telnet 服务器的连接。请稍候再试

telnet时显示&#xff1a;允许更多到 telnet 服务器的连接。请稍候再试 解决办法:windows自带telnet服务器默认的最大连接数为2&#xff0c;要想修改该设置&#xff0c;可以在命令行键入tlntadmn config maxconn要设置的连接数。最大连接数是指同一时刻内客户连接服务器的最大数…

Asp.net支持的最大上传文件大小

Asp.net的默认的最大可以上载的文件是4M,可以在web.config中配置.配置 ASP.NET HTTP 运行库设置。该节可以在计算机、站点、应用程序和子目录级别声明。 <configuration> <system.web> <httpRuntime><httpRuntime useFullyQualifiedRedirectUrl&q…

ASP.NET--Menu控件

http://www.meituan.com/r/i13110281 Menu控件提供静态和动态混合的菜单功能。在向页面添加这个控件的时候&#xff0c;开发人员可以选择将它设置为一个完全动态的菜单&#xff0c;以便整个站点的导航结构都可以显示在菜单中&#xff0c;类似于Windows的Start菜单。另一种选择是…

AI进军服装零售产业:微软小冰与特步推出定制化服装设计生产及零售平台

&#xff08;6月22日&#xff0c;北京&#xff09; 今日&#xff0c;体育用品企业特步集团与微软小冰宣布达成合作&#xff0c;依托微软小冰人工智能创造技术共同推出的定制化服装设计生产及零售平台即将上线。双方携手为消费者提供定制化图案设计&#xff0c;满足每个消费者的…

PHP如何通过Http Post请求发送Json对象数据?

因项目的需要&#xff0c;PHP调用第三方 Java/.Net 写好的 Restful Api&#xff0c;其中有些接口&#xff0c;需要 在发送 POST 请求时&#xff0c;传入对象。 Http中传输对象&#xff0c;最好的表现形式莫过于JSON字符串了&#xff0c;但是作为参数的接收方&#xff0c;又是需…