C#之旅

           走自己的路,让别人打的去吧。
随笔 - 66, 文章 - 0, 评论 - 117, 引用 - 2
数据加载中……

2006年5月13日

Microsoft PetShop 3.0 设计与实现 分析报告―――数据访问层

     摘要: Microsoft PetShop 3.0 设计与实现--数据访问层最近对多层设计实现和.Net产生了兴趣,从而研究了一下比较著名的多层范例程序――PetShop,现在的版本是3.0,和以前的版本从设计上已有一定的区别,应该是和Java的Petshop设计相当。关于一些Microsoft PetShop的来由、如何安装,所表现业务流程,数据库表结构等基本的信息的资料请大家参考下面文章http://... 阅读全文

posted @ 2006-05-13 16:17 c#之旅 阅读(531) | 评论 (0)编辑

PetShop3.x学习笔记1-Model

一、 Model主要功能:

1、              将每一个“业务实体”抽象成“(瘦数据)类”,可以很好地“划分”各个“对象”,操作更加清晰

2、              用于在应用程序各层之间传递数据,被用做传输数据的“容器”

3、              这就是所谓的“建模”过程!

4、              Model各个类(Model文件夹中的各个文件)划分或者说编制的原则,更趋向于模拟整个系统中的业务实体

 

二、 实现细节:

1、              为每一个Model中的类都标记了[Serializable],说明这些类可以被传行化,但是不能被继承!

2、              AccountInfo.cs文件:用户在网站注册的信息,及喜好选择情况

3、              使用构造函数可以初始化私有字段;使用属性可以读取私有字段(但使用属性不能设置私有字段的值)

4、              其中包含一个AddressInfo类的私有变量,和一个AddressInfo类的属性

5、              命名空间为PetShop.Model

6、              AddressInfo.cs文件:用户真实的个人姓名、住址和电话号码等信息

7、              AccountInfo不同的是,AddressInfo类允许使用属性设置私有变量的值

8、              CartItemInfo.cs文件:描述购物车中每一种所选商品的信息的类

9、              该“类”对象的某些信息(如这里的Subtotal属性)可能并不是此类的“自然信息”,而需要经过简单计算而得到!这些简单但必要的信息也要在类的设计中体现出来!

10、          CreditCardInfo.cs文件:表示特定一张信用卡的信息

11、          ItemInfo.cs文件:一个Item指的是category\product\item,如猫\波斯猫\成年男猫(或成年女猫)。这个文件表示一个Item的所有信息

12、          productDesc字段的作用?

13、          LineItemInfo.cs文件:注意与CartItemInfo类的区别!LineItemInfo是用来描述用户最终确认的订单当中的某一种类的产品的信息的类

14、          同样包括了Subtotal属性

15、          OrderInfo.cs文件:用于显示用户某一个订单具体信息的类,在此一个订单当中,可能包括多个商品种类,即包括多个LineItemInfo对象(实际上在OrderInfo类中也确实存在LineItemInfo类型对象的一个数组!)

16、          ProductInfo.cs文件:包括一个特定Product的信息,如波斯猫

 

三、 启发:

1、              来自Directory项目结束后的启发,以后做设计的时候,要将每个实体抽象为一个类,在整个系统中进行操作。

2、              在任意一个类当中,可能不只包括此实体类的自然信息,也可以包括一些对其他地方数据调用有用的属性信息,如根据数量和单价计算出来的总价属性,或者标志此实体的直属上级实体的属性

 

四、 问题:

1、              抽象这些业务实体模型为瘦数据类的原则是什么?什么样的业务实体可以被抽象,或者说进行抽象后更有意义?

2、              ItemInfo类中,productDesc字段的作用?

posted @ 2006-05-13 16:14 c#之旅 阅读(392) | 评论 (0)编辑

PetShop3.x学习笔记2-IDAL

一、 IDAL主要功能:

1、              这完全是工厂模式的一部分实现而已

2、              这是一组接口类,其中包括了每个要公开的数据访问方法。为每个数据库产品单独编写的DAL(数据访问层)都要实现这组接口所定义的方法

3、              也就是规定了在DAL中编写的对用户账号进行操作的类所必须执行的方法!

4、              IDAL要达到的目的是:实现业务逻辑(BLL)与数据库访问(DAL)的完全分离!!!

5、              IDAL各个类(IDAL文件夹中的各个文件)划分或者说编制的原则,更趋向于将对数据库的不同操作进行归类,考虑的主要方面是数据库操作!!!例如,有对用户账号进行的一系列数据库操作,则将这一系列操作统一放置于IAccount接口(将来实现后的Account类)文件当中

 

二、 实现细节:

1、              IAccount.cs文件:为针对不同数据库产品编写的操作用户账号的类所必须实现的一系列方法定义契约

2、              IInventory.cs文件:定义操作库存量的类所必须实现的一系列方法;或者说将操作库存量的一系列(所有)方法做一个汇总

3、              IItem.cs文件:定义操作某一Item的类所必须实现的一系列方法。(ItemModel中定义了,是指具体某一类别的Product,如男猫或女猫)

4、              对于其中的GetItemsByProduct()方法,返回的是一个ArrayList的接口类型(IList)的对象(见问题部分!)

5、              IOrder.cs文件:定义了一组DAL层中操作用户订单的类必须执行的方法。其中包括添加一张新订单的方法Insert()和根据一个已有的订单号取得此订单详细信息的方法GetOrder(),此方法返回的是一个OrderInfo对象。(Model中的OrderInfo类模型定义了用户的某一张Order中相关的信息,如发货地点,总价,信用卡号码等等)

6、              IProduct.cs文件:定义类一组在DAL层中编写的Product进行操作的类

7、              IProfile.cs文件:定义一组在DAL层编写的“对用户Profile进行操作的类”

 

三、 启发:

1、              这样就可以让在BLL层只针对IDAL层定义的接口进行编程(使用IDAL接口定义的这些方法)就可以了!!!无论在底层使用了什么厂家的数据库产品,有区别的只是针对此数据库产品编写的DAL层(相同的方法,如SignIn()方法,对于不同的数据库产品,可能有不同的实现方式!),而不会影响到上层的BLL层已经编写好的内容!

2、              从这里可以看到软件架构师和程序员工作上的区别!架构师要负责的是搭建系统的层次结构,定义接口;而程序员要负责的是针对接口的具体代码实现过程!

3、              这个IDAL接口的使用,主要是为了保证在底层数据库实现,甚至数据库产品发生变化的时候,不需要对上层BLL层的业务逻辑进行大量的修改!BLL层针对IDAL接口编程即可!!!

4、              IDAL文件夹中定义的所有的接口中的所有的方法,包括了整个程序要对数据库进行操作的所有方法

5、              由于PetShop只是一个演示程序,所以若对数据库操作的某一类别(如对Account进行操作)中定义的操作类不够用(如除了接口中定义的几个方法外还需要其他Account操作),还可以在接口中追加其他的方法,用以约束DAL层的实现类(如Account类)必须执行这些新增加的方法!

 

四、 问题:

1、              定义这组接口后,如何保证为每个单独的数据库产品编写的DAL都执行这组接口所定义的方法?(答案:编写DAL的时候必须刻意保证,否则就失去了定义IDAL层分割BLLDAL层的意义!!!)

2、              既然在实现GetItemsByProduct()方法的时候,也是要把返回的所有Item对象添加到一个ArrayList当中去,但为什么GetItemsByProduct()方法返回的不是一个ArrayList对象,而是一个IList接口的对象呢?仅仅是基类引用的方法吗?这里的基类引用有什么用呢?是否是由于为了不限制使用的数组形式?执行IList则可以使用ArrayList,也可以使用其他形式的数组???

3、              IProduct.cs文件中为何要将查询条件参数定义为一个string类型的数组?

posted @ 2006-05-13 16:14 c#之旅 阅读(408) | 评论 (0)编辑

PetShop3.x学习笔记3-Utility

一、 Utility主要功能:

1、              一组帮助器类,包含DPAPI的包装(不理解)

2、              有一组负责加密和解密的公共方法

3、              使用了Crypt32.dllkernel32.dll中的方法

4、              ConnectionInfo类中使用了两个方法,分别用于加密和解密数据库连接字符串

5、             Utility中的两个文件,合作完成了加密和解密数据库连接字符串的功能!!!

6、             可以在自己的项目中直接使用这些程序!!!

7、              似乎是使用ConnectionInfo类中的加密/解密方法,包装DataProtector类中的真正的使用加密API”进行加密/解密的方法

8、              还需要在后面对BLLDAL的学习中,进一步深入理解Utility的作用

 

二、 实现细节:

1、              ConnectionInfo.cs文件中:

2、              注意这里判断(输入)字符串是否为空的方法:if(InputString.Equals(string.Empty))

3、              用来加密/解密的API是基于Byte(字节(数组))进行工作的!

 

三、 启发:

1、              帮助我形成了在做项目过程中逐步积累的知识漏洞列表

2、              Attribute的作用:见《C# Professional 2nd EditionP257

 

四、 问题:

1、              System.Runtime.InteropServices包含什么内容?其中的类是什么功能?

2、              DataProtector类实现了什么功能?

3、              Convert.ToBase64String()方法,将输入的什么转化为了什么

posted @ 2006-05-13 16:12 c#之旅 阅读(224) | 评论 (0)编辑

[转] PetShop3.x学习笔记4-SQLServer_DB Architec Design

一、 SQLServer_DB Architec主要功能:

1、              数据库架构设计

 

二、 实现细节:

1、              注意看,什么是数据库架构图!!!

2、              使用SQLServer自带的“数据库关系图”功能直接就可以进行数据库建模!!!!!!在数据库关系图中生成的关系图实际上就是数据模型,可以直接保存回数据库,创建相应的表及表间的关系!!!!!!

3、              学习了给单独的数据库分配管理员和管理权限!!!!!!

 

三、 启发:

1、              在设计数据库表的时候,如果添加了“外键关系”,则在进行Visio反向工程的时候就可以显示出来这个关系

2、              可以看到,“关系”是可以在程序(如ADO.NETDataSet)中写入的,还可以在进行数据库设计的过程中进行定义。关系也可以看作“实体”进行操作!

3、              要研究使用Visio进行数据库建模,并将完成的数据模型直接创建在SQLServer中的方法

 

四、 问题:

1、              在做自己程序的数据库设计的时候,也要将ID字段设置为varchar(10)PetShop这样设计的目的是什么?

2、              我用Visio做的数据库建模,如何直接在SQLServer数据库中生成表和关系?

3、             数据库架构图是怎么得到的???(上边已有答案)

4、              问题来了,为什么要这样进行数据库建模?!



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=609080

posted @ 2006-05-13 16:11 c#之旅 阅读(198) | 评论 (0)编辑

PetShop3.x学习笔记5-我读cocoboy79《MS PetShop 3.x 设计与实现——数据访问层》

     摘要: 红色——文档重点;蓝色——我自己的理解;绿色——不明白的地方----------------------------------------------------------------------------------MS PetShop 3.x 设计与实现——数据访问层最近对多层设计实现和.Net产生了兴趣,从而研究了一下比较著名的多层范例程序――PetShop,现在的版本是3.0,和以前... 阅读全文

posted @ 2006-05-13 16:06 c#之旅 阅读(288) | 评论 (2)编辑

PetShop3.x学习笔记6-SQLServer学习笔记

     摘要: 一、 SQLServer主要功能:1、 这是Microsoft SQL Server特定的PetShop DAL层实现,执行了IDAL接口定义的各方法二、 实现细节:1、 SQLHelper.cs文件:就是MS DAAB2、 主要定义了以下几个方法对数据库操作:3、 ExecuteNonQuery()方法用于根据用户的要求(SQL语句)对数据库执行操作,这包括3个重载方法4、 注意:Execute... 阅读全文

posted @ 2006-05-13 16:03 c#之旅 阅读(302) | 评论 (0)编辑

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

专栏作品
对比.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,谢谢。

posted @ 2006-05-13 09:20 c#之旅 阅读(487) | 评论 (0)编辑

Xp下安装SQL2000或者其他软件系统提示之前有挂起的安装操作,要求重启系统的解决办法

在Windwos Xp下,要安装Sqlsp补丁以及其他软件,尤其是微软自己的软件的时候,系统提示之前有挂起的安装操作,要求重启,这里往往重启无用,解决办法:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager

删除PendingFileRenameOperations
再重新安装就可以了.

posted @ 2006-05-13 07:42 c#之旅 阅读(3469) | 评论 (13)编辑