PetShop3.x学习笔记5-我读cocoboy79《MS PetShop 3.x 设计与实现——数据访问层》
2007-04-28 16:20
483 查看
红色——文档重点;蓝色——我自己的理解;绿色——不明白的地方
----------------------------------------------------------------------------------
MS PetShop 3.x 设计与实现——数据访问层
最近对多层设计实现和.Net产生了兴趣,从而研究了一下比较著名的多层范例程序――PetShop,现在的版本是3.0,和以前的版本从设计上已有一定的区别,应该是和Java的Petshop设计相当。
关于一些Microsoft PetShop的来由、如何安装,所表现业务流程,数据库表结构等基本的信息的资料请大家参考下面文章
http://msdn.microsoft.com/library/en-us/dnbda/html/bdasamppet.asp
另外建议先看一下这篇文章:
http://msdn.microsoft.com/library/en-us/dnbda/html/petshop3x.asp
本文将以设计和实现紧密结合的方式来分析,这也是我们广大实践型的软件开发人员!!!的风格。先看一下设计图和具体实现VS.NET工程的表格。
MSPetShop 3.0 系统结构图:
从图中可以看到系统大体分为Presentation,Business Logic,Data Access 三层,每层中又有子层。每层(也包括子层)各司其职,又互相协作,本文顺序以此图为准,从下到上分析。
对应上图,具体的.NET Project实现列表(借用MS文章中的列表不用翻译了吧)
另外我写这篇文章时是一边看源码一边写,所以建意大家最好安装一个Petshop3,因为时间仓促,水平有限,如我有不对之处请给我发Email更正。email:cocoboy79@163.com qq:364941
----------------------------------------------------------------------------------
首先我们来看一下DAL层。
一:Data Access Layer:
1、PetShop.Utility如下图:(上表中Utility为其实现工程)
正如上表所描述,这个名字空间有两个类,一个是ConnectionInfo,用于加密/解密数据库连接信息;另一个DataProtector,它调用了Crypt32.dll和kernel32.dll实现一些底层数据安全操作,这个类要在下面的PetShop.XXXDAL名字空间中调用,可见Petshop.Utility只起到了“数据访问辅助工具”的作用!!!
2、PetShop.SQLServerDAL ――系统结构图中DAL层中的SqlServer DAL子层实现
SqlHelper类(SqlHelper类就是DAAB子层!!!)实际上是封装了关于此系统中数据库操作访问的一些常用功能,其中它还会调用上面的PetShop.Utility中的ConectionInfo类方法加密解密连接字符串,如:ConnectionInfo.DecryptDBConnectionString方法。
SqlHelper类是基于Microsoft Data Access Application Block for .NET(MS DAAB)。这个东西是用来帮助用户更好的在.NET的访问数据(何为“更好地”??)。如MS一段话:
Are you involved in the design and development of data access code for .NET-based applications? Have you ever felt that you write the same data access code again and again? Have you wrapped data access code in helper functions that let you call a stored procedure in one line? If so, the Microsoft® Data Access Application Block for .NET is for you
(要明确DAAB或是SQLHelper的作用???!!!两者实现的功能相同?)
其实可以自已写一个类似SqlHelper的东西,以实现一般化的对数据库的操作,以在各项目中重用,当然也可以使用现在(PetShop中)的MS为你做好的这个SqlHelper或是Microsoft® Data Access Application Block for .NET(DAAB或SQLHelper是可重用的?如何重用?还是要等明确功能后再分析!!!),避免不同项目中总是写同样的重复的数据库访问程序。有时间最好还是看一下SqlHelper的具体程序实现思路以及所提到的那个Microsoft Data Access Application Block for .NET(路子正确!)。不过这里我们的SqlHelper应该只是部分实现。更全面信息请参看:http://msdn.microsoft.com/library/en-us/dnbda/html/daab-rm.asp
Account类对用户账户进行操作如Insert、Update、SignIn,其中这些对数据库的操作,使用了上面的SqlHelper类来实现。另外,Inventory、Order、Product和Profile类与Account类都是采用同样的方法对数据库相关表进行操作,程序风格一致。这些类中对数据库的操作都是通过此名字空间下的SqlHelper类进行的。
例如,下面语句:
定义“向SignOn表中插入数据的SQL语句”为一个常量,但其中的“用户名”和“密码”是作为变量出现的
为什么能在字符串中定义变量呢?使用“@”的是什么写法?!
private const string SQL_INSERT_SIGNON = "INSERT INTO SignOn VALUES (@UserId, @Password)";
private const string PARM_USER_ID = "@UserId";
private const string PARM_PASSWORD = "@Password";
来定义一个sql语句,以及声明其中可变参数,然后像下面这样用SqlHelper类的合适的方法执行:
SQLHelper.ExecuteNonQuery(trans, CommandType.Text, SQL_INSERT_SIGNON, signOnParms);
(1)SQLHelper类实现的功能:如果把整个对数据库进行操作看作是做菜的话,则SQL语句就是菜谱,指明了如何对数据库进行操作;“连接字符串connString”、“命令类型cmdType”和“变量cmdParms”都是做菜的原料;而SQLHelper类中的ExecuteNonQuery()方法就是做菜(烹饪)的过程!!!
(2)ExecuteNonQuery()使用了ADO.NET的语法,根据用户提供的环境信息(各种原料),执行对数据库的操作,并返回操作成功与否的结果标志!!!
(3)在这里可以看到,如果整个系统使用的是存储过程,而不是SQL语句,应该怎么修改!!!
(4)DAAB的功能就类似于在Directory项目中的SQLDBIO类的作用!!!现在看到了规范的做法——SQLDBIO实际就是MS DAAB的雏形!!!
在SQLHelper.ExecuteNonQuery()实现中,最终还是调用了ADO.NET中的相关类,最终执行对数据库的操作,可见SqlHelper在这里又封装了一下ADO.NET相关类,以优化数据操作。正如SqlHelper.cs中注释提示:
The SqlHelper class is intended(预期的) to encapsulate(压缩) high performance(执行), scalable(攀登) best practices for common uses of SqlClient.
下面是SqlHelper. ExecuteNonQuery的实现内容:
public static int ExecuteNonQuery(string connString, CommandType cmdType, string cmdText, params SqlParameter[] cmdParms)
{
//注:运行时cmdText的实参就是SQL_INSERT_SIGNON
SqlCommand cmd = new SqlCommand();
using (SqlConnection conn = new SqlConnection(connString))
{
PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
int val = cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
return val;
}
}
另外Inventory、Order、Product和Profile与Account类的声明都是像public class Account : IAccount这样实现某个相关的接口,像IAccount这样的接口是在PetShop.IDAL中声明的,见后面介绍。
3、PetShop.OracleDAL ―――系统结构图中 DAL层的OracleDAL子层实现
个人认为结构应该同上面的PetShop. SQLServerDAL,另外SqlHelper变成了OraHelper,在OraHelper中当然具体实现了对特定的Oracle数据库的联接操作,看一下源程序很明显原来的 SqlCommand cmd = new SqlCommand(); 变成了OracleCommand cmd = new OracleCommand();。
注意一下:在系统结构图中的DAL层还有两个XXXDAAB的子层,它们对应的实现在哪里呢?下面对应一下:
以下的左边是图中 DataAccessLayer的各部分,右边是具体实现所在名字空间或类
SqlServer DAL――PetShop.SQLServerDAL名字空间
Sql DAAB――PetShop.SqlServerDal.SqlHelper类
Oracle DAL――PetShop.OracleDAL名字空间
Oracle DAAB――PetShop.OracleDAL.OraHelper类
4、PetShop.IDAL 数据访问接口――对应系统结构图中DAL Interface
作者总结的“接口”的定义和作用:
接口是一系列‘功能’的声明或名单;接口没有实现细节;
如下接口IAccount定义也可以看出IAccount只有方法声明,而没有实现细节:
using System;
using PetShop.Model;
namespace PetShop.IDAL
{
// Inteface for the Account DAL
public interface IAccount
{
// Authenticate a user
AccountInfo SignIn(string userId, string password);
/// Get a user's address stored in the database
AddressInfo GetAddress(string userId);
/// Insert an account into the database
void Insert(AccountInfo account);
/// Update an account in the database
void Update(AccountInfo Account);
}
}
您只需要调用接口,而不用管接口是如何实现的!!!那么如果接口没有实现,调用它有什么用?实际上接口的实现是由某个类来做的。
在这里,IAccount接口是由PetShop.SqlServerDAL.Account类,或是PetShop.OracleDAL.Account类来实现的。从他们的定义可以看到:
public class Account : IAccount {…….}
为什么是两个类都实现同一接口又是‘或’呢?因为这里使用接口的目的就是为了统一‘外观’,当上层BLL层调用此接口方法时,不用知道这个接口是由哪个类具体实现的。那谁来确定使用哪个类进行实现?请再看下面。
(PetShop.IDAL下的其它接口和IAccount一样,故在此略过。)
5、PetShop.DALFactory 数据访问工厂
工厂模式是设计模式的一种,以我理解就像Factory这个词一样,对于用户来说,工厂里产品如何生产的你不用知道,你只要去用工厂里生产出来的东西就可以了。
MSPetShop3.0用工厂模式来实现了对SqlServer和Oracle数据库访问的操作,而用户(business Logic Layer)不用知道也不用关心后台用的是哪一种数据库,它只要用接口就行了,接口中定义了要用的方法,注意:当调用接口时会根据具体的情况再去调用底层数据访问操作!!!
而现在这个DALFactory就是关键,当BLL层要操作数据库时,DALFactory会根据具体情况使用本文上面介绍的SqlServerDAL和OracleDAL中的一个。这样系统上层只管调用,而下层来实现细节;上级只管发号施令,下级去干活。对于上层来说实现细节被隐藏了。
那么DALFactory是如何决定应该用SqlServerDAL还是用OracleDAL的呢???我们接着分析。
以下是PetShop.DALFactory.Account类的实现:
namespace PetShop.DALFactory
{
// Factory implementation for the Account DAL object
public class Account
{
public static PetShop.IDAL.IAccount Create() //这里返回接口
{
/// Look up the DAL implementation we should be using
string path = System.Configuration.ConfigurationSettings.AppSettings["WebDAL"];
string className = path + ".Account";
// Using the evidence given in the config file load the appropriate assembly and class
注意:返回的不是“接口的实例”!!!这里使用的是“基类引用”的方式!!!
Assembly.Load()方法是在什么地方定义的???
Assembly.CreateInstance()呢?
return (PetShop.IDAL.IAccount)Assembly.Load(path).CreateInstance(className);
}
}
}
以下则是web.config中<appSettings>节点中的一部分:
<add key="WebDAL" value="PetShop.SQLServerDAL" />
<add key="OrdersDAL" value="PetShop.SQLServerDAL" />
<add key="Event Log Source" value=".NET Pet Shop" />
上面的Create()方法返回IAccount接口,用System.Configuration.ConfigurationSettings.AppSettings["WebDAL"];则可以得到Web.config的<appsettings>节点中的关于系统中应该使用哪个数据访问层(SqlserverDAL还是OracleDAL)的信息。因为我在安装PetShop3.0时选择的是Sqlserver所以在此是:value="PetShop.SQLServerDAL",如果用的是Oracle那就是value="PetShop.OracleDAL" 了吧!而且这个文件也应该是可以更改的。接下来className=path+”.Account”返回的应该是PetShop.SQLServerDAL.Account,然后再用Assembly.Load加载PetShop.SQLServerDAL.dll,同时创建PetShop.SQLServerDAL.Account的实例,并以接口(PetShop.IDAL.IAccount)类型返回。这样BLL调用IAccount接口时就会用PetShop.SQLServerDAL.Account类的实现代码。(回上面第4再看一下)
看!这样根据系统当前Web.config文件的配置描述(这也应该是系统运行时实际的配置),BLL层只要像下面这样:
// Get an instance of the account DAL using the DALFactory
注意:这里是在BLL中使用“如此这般设计模式的数据访问功能”的方法!!!
这里解答了以前的创建实例,调用方法的问题。
看仔细:这里实际创建的是“适当的”DAL层的对应account类的实例,而使用接口的方式返回给BLL进行调用。对接口应用的方法,实际是对“适当的”DAL层的类进行的操作!
注意写法:只有通过PetShop.DALFactory.Account.Create()创建的接口返回给BLL后,才能实现“对接口应用方法,实际是对‘适当的’DAL层的对象的方法进行的操作”的效果!!!
IAccount dal = PetShop.DALFactory.Account.Create();
AccountInfo account = dal.SignIn(userId, password);
//看看上面第4点的IAccount接口
就可以直接调用接口方法通过下层DAL层操作数据库了(在此具体为用户账号相关操作),而BLL层并不用知道应该通过SqlserverDAL还是OracleDAL访问数据库,这由都DAL Factory决定,你用的是什么数据库以及底层细节,更不用BLL知道,这样做的好处是:对于BLL层以及更上层的程序,不会或很少会因为底层程序的变动而受影响。BLL层中调用接口就行了,只要那个接口定义没变,一切仍然OK.
6、PetShop.ConfigTool
首先在../Microsoft/PetShop/ConfigTool/中有一个app.config文件,看一下其中内容,分别定义了两种数据库的联接字符串,在app.config中有一行
<add key="WebConfigFileLocation" value="Web/Web.config" />
则标识出给asp.net程序使用的web.config配置文件的相对位置。然后看一下PetShopConnectionString的EncryptConnectionString方法的源码,这个类中先是从当前目录的app.config文件中读出web.config文件的位置,如下:
public static readonly string CONFIGFILE = ConfigurationSettings.AppSettings["WebConfigFileLocation"];
然后语句
XmlDocument doc = new XmlDocument();
doc.Load(CONFIGFILE);
加载Web.config文件,最后将加密的连接字符串写入Web.config对应的XML节点中。以供Asp.net应用程序使用。其中的加密还是使用PetShop.Utility。
而ConfigConsole,调用PetShopConnectionString类EncryptConnectionString执行对联接字符串进行加密。另外PetShopEventLog类也是在ConfigConsole中使用的。用于记录程序日志。
所以在最后部署时Web.config的连接字符串是加密的。
7、PetShop.Model 业务实体模型
在SqlServerDAL和OracleDAL中都使用了Model
无论怎样,上层的程序执行最终结果都是要操作数据库,而数据库是关系型的,不是面向对象的,那就得把‘平面的’‘表’结合业务规则抽象成类(这里说的是将一张数据库表抽象为一个类,但是这里并没有继承DataTable类啊!!!),这样想办法让上层(BLL及以上)以为自已在操作类而不是数据库表,从而使‘它们’感觉没有数据库的存在,上层只管面向对象编程就可以了。
这类似现在所说的O-R MAPPING,但O-R MAPPING比这种简单的数据到对象的持久化要复杂。据说可以在表结构有变化的情况下,上层应用程序代码不用更改,只要改O-R MAPPING的相关设置就可以了。
上面第2节中已看到Petshop的SqlServerDal和OracleDal中定义的sql语句,然后根据上层的调用,把sql语句传给SqlHelper执行,再来看看SqlServerDal的一段程序:
private void SetAccountParameters(SqlParameter[] parms, AccountInfo acc)
{
parms[0].Value = acc.Email;
parms[1].Value = acc.Address.FirstName;
parms[2].Value = acc.Address.LastName;
parms[3].Value = acc.Address.Address1;
parms[4].Value = acc.Address.Address2;
parms[5].Value = acc.Address.City;
parms[6].Value = acc.Address.State;
parms[7].Value = acc.Address.Zip;
parms[8].Value = acc.Address.Country;
parms[9].Value = acc.Address.Phone;
parms[10].Value = acc.UserId;
}
注意:当在BLL中对Account实体进行OOP之后,某个Account对象的各个字段就存储了关于此Account的各个信息。而这些信息存储在这个Account对象中是无法更新到数据库当中去的!所以,要将此Account对象的各个字段“赋值”给一个SqlParameter[]数组,像本文第4页我写的,将此数组作为一个“做菜的原料”,然后使用DAAB直接“烹饪”了即可。“烹饪”之后,在BLL中对此Account对象进行的更改才会真正保存到数据库当中!!!!!!
parms[x]就是那些:声明为常量的 有参数的Sql语句中的 参数。
这里用Model中的AccountInfo的各Field和这些参数Mapping。所以对于BLL层,只知道这些Model就可以了(只对各Model的各字段赋值或操作就可以了),反正最后在SqlServerDAL或是OracleDAL中Model的成员们(既“字段们”)会Mapping到参数中以存取数据库(为什么不直接Mapping到数据集的字段呢?我想应该是因为这里数据库操作直接给SqlHeper的原因,不然就没必要用SqlHelper了。于是才又多了图中的xxx DAAB层,这样Mapping给参数再传给DAAB来处理)
Data Access Layer总结:
(1) DAL完成数据库访问任务,上层(BLL)直需调用接口即可,不用知道具体访问细节
(2) 用Factory模式实现,使用在运行时读取web.config的方法来得到连接配置信息
(3) 选用SqlServerDAL或OracleDAL之一的相对具体子层,同时使用SqlHelper或OraHelper之一来完成数据库操作
----------------------------------------------------------------------------------
MS PetShop 3.x 设计与实现——数据访问层
最近对多层设计实现和.Net产生了兴趣,从而研究了一下比较著名的多层范例程序――PetShop,现在的版本是3.0,和以前的版本从设计上已有一定的区别,应该是和Java的Petshop设计相当。
关于一些Microsoft PetShop的来由、如何安装,所表现业务流程,数据库表结构等基本的信息的资料请大家参考下面文章
http://msdn.microsoft.com/library/en-us/dnbda/html/bdasamppet.asp
另外建议先看一下这篇文章:
http://msdn.microsoft.com/library/en-us/dnbda/html/petshop3x.asp
本文将以设计和实现紧密结合的方式来分析,这也是我们广大实践型的软件开发人员!!!的风格。先看一下设计图和具体实现VS.NET工程的表格。
MSPetShop 3.0 系统结构图:
从图中可以看到系统大体分为Presentation,Business Logic,Data Access 三层,每层中又有子层。每层(也包括子层)各司其职,又互相协作,本文顺序以此图为准,从下到上分析。
对应上图,具体的.NET Project实现列表(借用MS文章中的列表不用翻译了吧)
Project | Purpose |
BLL | Home for business logic components |
ConfigTool | Administration application used to encrypt connection strings and create event log source |
DALFactory | Classes used to determine which database access assembly to load |
IDAL | Set of interfaces which need to be implemented by each DAL implementation |
Model | Thin data classes or business entities |
OracleDAL | Oracle specific implementation of the Pet Shop DAL which uses the IDAL interfaces |
Post-Build | Project to run post compile actions such as adding assemblies to the GAC or COM+ |
Pre-Build | Project to remove assemblies from the GAC or unregister assemblies from COM+ |
SQLServerDAL | Microsoft SQL Server specific implementation of the Pet Shop DAL which uses the IDAL interfaces |
Utility | Set of helper classes including a wrapper for the DPAPI |
Web | Web pages and controls |
Solution Items | Miscellaneous items used to build the application such as Pet Shop.snk key file used to sign application assemblies |
----------------------------------------------------------------------------------
首先我们来看一下DAL层。
一:Data Access Layer:
1、PetShop.Utility如下图:(上表中Utility为其实现工程)
正如上表所描述,这个名字空间有两个类,一个是ConnectionInfo,用于加密/解密数据库连接信息;另一个DataProtector,它调用了Crypt32.dll和kernel32.dll实现一些底层数据安全操作,这个类要在下面的PetShop.XXXDAL名字空间中调用,可见Petshop.Utility只起到了“数据访问辅助工具”的作用!!!
2、PetShop.SQLServerDAL ――系统结构图中DAL层中的SqlServer DAL子层实现
SqlHelper类(SqlHelper类就是DAAB子层!!!)实际上是封装了关于此系统中数据库操作访问的一些常用功能,其中它还会调用上面的PetShop.Utility中的ConectionInfo类方法加密解密连接字符串,如:ConnectionInfo.DecryptDBConnectionString方法。
SqlHelper类是基于Microsoft Data Access Application Block for .NET(MS DAAB)。这个东西是用来帮助用户更好的在.NET的访问数据(何为“更好地”??)。如MS一段话:
Are you involved in the design and development of data access code for .NET-based applications? Have you ever felt that you write the same data access code again and again? Have you wrapped data access code in helper functions that let you call a stored procedure in one line? If so, the Microsoft® Data Access Application Block for .NET is for you
(要明确DAAB或是SQLHelper的作用???!!!两者实现的功能相同?)
其实可以自已写一个类似SqlHelper的东西,以实现一般化的对数据库的操作,以在各项目中重用,当然也可以使用现在(PetShop中)的MS为你做好的这个SqlHelper或是Microsoft® Data Access Application Block for .NET(DAAB或SQLHelper是可重用的?如何重用?还是要等明确功能后再分析!!!),避免不同项目中总是写同样的重复的数据库访问程序。有时间最好还是看一下SqlHelper的具体程序实现思路以及所提到的那个Microsoft Data Access Application Block for .NET(路子正确!)。不过这里我们的SqlHelper应该只是部分实现。更全面信息请参看:http://msdn.microsoft.com/library/en-us/dnbda/html/daab-rm.asp
Account类对用户账户进行操作如Insert、Update、SignIn,其中这些对数据库的操作,使用了上面的SqlHelper类来实现。另外,Inventory、Order、Product和Profile类与Account类都是采用同样的方法对数据库相关表进行操作,程序风格一致。这些类中对数据库的操作都是通过此名字空间下的SqlHelper类进行的。
例如,下面语句:
定义“向SignOn表中插入数据的SQL语句”为一个常量,但其中的“用户名”和“密码”是作为变量出现的
为什么能在字符串中定义变量呢?使用“@”的是什么写法?!
private const string SQL_INSERT_SIGNON = "INSERT INTO SignOn VALUES (@UserId, @Password)";
private const string PARM_USER_ID = "@UserId";
private const string PARM_PASSWORD = "@Password";
来定义一个sql语句,以及声明其中可变参数,然后像下面这样用SqlHelper类的合适的方法执行:
SQLHelper.ExecuteNonQuery(trans, CommandType.Text, SQL_INSERT_SIGNON, signOnParms);
(1)SQLHelper类实现的功能:如果把整个对数据库进行操作看作是做菜的话,则SQL语句就是菜谱,指明了如何对数据库进行操作;“连接字符串connString”、“命令类型cmdType”和“变量cmdParms”都是做菜的原料;而SQLHelper类中的ExecuteNonQuery()方法就是做菜(烹饪)的过程!!!
(2)ExecuteNonQuery()使用了ADO.NET的语法,根据用户提供的环境信息(各种原料),执行对数据库的操作,并返回操作成功与否的结果标志!!!
(3)在这里可以看到,如果整个系统使用的是存储过程,而不是SQL语句,应该怎么修改!!!
(4)DAAB的功能就类似于在Directory项目中的SQLDBIO类的作用!!!现在看到了规范的做法——SQLDBIO实际就是MS DAAB的雏形!!!
在SQLHelper.ExecuteNonQuery()实现中,最终还是调用了ADO.NET中的相关类,最终执行对数据库的操作,可见SqlHelper在这里又封装了一下ADO.NET相关类,以优化数据操作。正如SqlHelper.cs中注释提示:
The SqlHelper class is intended(预期的) to encapsulate(压缩) high performance(执行), scalable(攀登) best practices for common uses of SqlClient.
下面是SqlHelper. ExecuteNonQuery的实现内容:
public static int ExecuteNonQuery(string connString, CommandType cmdType, string cmdText, params SqlParameter[] cmdParms)
{
//注:运行时cmdText的实参就是SQL_INSERT_SIGNON
SqlCommand cmd = new SqlCommand();
using (SqlConnection conn = new SqlConnection(connString))
{
PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
int val = cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
return val;
}
}
另外Inventory、Order、Product和Profile与Account类的声明都是像public class Account : IAccount这样实现某个相关的接口,像IAccount这样的接口是在PetShop.IDAL中声明的,见后面介绍。
3、PetShop.OracleDAL ―――系统结构图中 DAL层的OracleDAL子层实现
个人认为结构应该同上面的PetShop. SQLServerDAL,另外SqlHelper变成了OraHelper,在OraHelper中当然具体实现了对特定的Oracle数据库的联接操作,看一下源程序很明显原来的 SqlCommand cmd = new SqlCommand(); 变成了OracleCommand cmd = new OracleCommand();。
注意一下:在系统结构图中的DAL层还有两个XXXDAAB的子层,它们对应的实现在哪里呢?下面对应一下:
以下的左边是图中 DataAccessLayer的各部分,右边是具体实现所在名字空间或类
SqlServer DAL――PetShop.SQLServerDAL名字空间
Sql DAAB――PetShop.SqlServerDal.SqlHelper类
Oracle DAL――PetShop.OracleDAL名字空间
Oracle DAAB――PetShop.OracleDAL.OraHelper类
4、PetShop.IDAL 数据访问接口――对应系统结构图中DAL Interface
作者总结的“接口”的定义和作用:
接口是一系列‘功能’的声明或名单;接口没有实现细节;
如下接口IAccount定义也可以看出IAccount只有方法声明,而没有实现细节:
using System;
using PetShop.Model;
namespace PetShop.IDAL
{
// Inteface for the Account DAL
public interface IAccount
{
// Authenticate a user
AccountInfo SignIn(string userId, string password);
/// Get a user's address stored in the database
AddressInfo GetAddress(string userId);
/// Insert an account into the database
void Insert(AccountInfo account);
/// Update an account in the database
void Update(AccountInfo Account);
}
}
您只需要调用接口,而不用管接口是如何实现的!!!那么如果接口没有实现,调用它有什么用?实际上接口的实现是由某个类来做的。
在这里,IAccount接口是由PetShop.SqlServerDAL.Account类,或是PetShop.OracleDAL.Account类来实现的。从他们的定义可以看到:
public class Account : IAccount {…….}
为什么是两个类都实现同一接口又是‘或’呢?因为这里使用接口的目的就是为了统一‘外观’,当上层BLL层调用此接口方法时,不用知道这个接口是由哪个类具体实现的。那谁来确定使用哪个类进行实现?请再看下面。
(PetShop.IDAL下的其它接口和IAccount一样,故在此略过。)
5、PetShop.DALFactory 数据访问工厂
工厂模式是设计模式的一种,以我理解就像Factory这个词一样,对于用户来说,工厂里产品如何生产的你不用知道,你只要去用工厂里生产出来的东西就可以了。
MSPetShop3.0用工厂模式来实现了对SqlServer和Oracle数据库访问的操作,而用户(business Logic Layer)不用知道也不用关心后台用的是哪一种数据库,它只要用接口就行了,接口中定义了要用的方法,注意:当调用接口时会根据具体的情况再去调用底层数据访问操作!!!
而现在这个DALFactory就是关键,当BLL层要操作数据库时,DALFactory会根据具体情况使用本文上面介绍的SqlServerDAL和OracleDAL中的一个。这样系统上层只管调用,而下层来实现细节;上级只管发号施令,下级去干活。对于上层来说实现细节被隐藏了。
那么DALFactory是如何决定应该用SqlServerDAL还是用OracleDAL的呢???我们接着分析。
以下是PetShop.DALFactory.Account类的实现:
namespace PetShop.DALFactory
{
// Factory implementation for the Account DAL object
public class Account
{
public static PetShop.IDAL.IAccount Create() //这里返回接口
{
/// Look up the DAL implementation we should be using
string path = System.Configuration.ConfigurationSettings.AppSettings["WebDAL"];
string className = path + ".Account";
// Using the evidence given in the config file load the appropriate assembly and class
注意:返回的不是“接口的实例”!!!这里使用的是“基类引用”的方式!!!
Assembly.Load()方法是在什么地方定义的???
Assembly.CreateInstance()呢?
return (PetShop.IDAL.IAccount)Assembly.Load(path).CreateInstance(className);
}
}
}
以下则是web.config中<appSettings>节点中的一部分:
<add key="WebDAL" value="PetShop.SQLServerDAL" />
<add key="OrdersDAL" value="PetShop.SQLServerDAL" />
<add key="Event Log Source" value=".NET Pet Shop" />
上面的Create()方法返回IAccount接口,用System.Configuration.ConfigurationSettings.AppSettings["WebDAL"];则可以得到Web.config的<appsettings>节点中的关于系统中应该使用哪个数据访问层(SqlserverDAL还是OracleDAL)的信息。因为我在安装PetShop3.0时选择的是Sqlserver所以在此是:value="PetShop.SQLServerDAL",如果用的是Oracle那就是value="PetShop.OracleDAL" 了吧!而且这个文件也应该是可以更改的。接下来className=path+”.Account”返回的应该是PetShop.SQLServerDAL.Account,然后再用Assembly.Load加载PetShop.SQLServerDAL.dll,同时创建PetShop.SQLServerDAL.Account的实例,并以接口(PetShop.IDAL.IAccount)类型返回。这样BLL调用IAccount接口时就会用PetShop.SQLServerDAL.Account类的实现代码。(回上面第4再看一下)
看!这样根据系统当前Web.config文件的配置描述(这也应该是系统运行时实际的配置),BLL层只要像下面这样:
// Get an instance of the account DAL using the DALFactory
注意:这里是在BLL中使用“如此这般设计模式的数据访问功能”的方法!!!
这里解答了以前的创建实例,调用方法的问题。
看仔细:这里实际创建的是“适当的”DAL层的对应account类的实例,而使用接口的方式返回给BLL进行调用。对接口应用的方法,实际是对“适当的”DAL层的类进行的操作!
注意写法:只有通过PetShop.DALFactory.Account.Create()创建的接口返回给BLL后,才能实现“对接口应用方法,实际是对‘适当的’DAL层的对象的方法进行的操作”的效果!!!
IAccount dal = PetShop.DALFactory.Account.Create();
AccountInfo account = dal.SignIn(userId, password);
//看看上面第4点的IAccount接口
就可以直接调用接口方法通过下层DAL层操作数据库了(在此具体为用户账号相关操作),而BLL层并不用知道应该通过SqlserverDAL还是OracleDAL访问数据库,这由都DAL Factory决定,你用的是什么数据库以及底层细节,更不用BLL知道,这样做的好处是:对于BLL层以及更上层的程序,不会或很少会因为底层程序的变动而受影响。BLL层中调用接口就行了,只要那个接口定义没变,一切仍然OK.
6、PetShop.ConfigTool
首先在../Microsoft/PetShop/ConfigTool/中有一个app.config文件,看一下其中内容,分别定义了两种数据库的联接字符串,在app.config中有一行
<add key="WebConfigFileLocation" value="Web/Web.config" />
则标识出给asp.net程序使用的web.config配置文件的相对位置。然后看一下PetShopConnectionString的EncryptConnectionString方法的源码,这个类中先是从当前目录的app.config文件中读出web.config文件的位置,如下:
public static readonly string CONFIGFILE = ConfigurationSettings.AppSettings["WebConfigFileLocation"];
然后语句
XmlDocument doc = new XmlDocument();
doc.Load(CONFIGFILE);
加载Web.config文件,最后将加密的连接字符串写入Web.config对应的XML节点中。以供Asp.net应用程序使用。其中的加密还是使用PetShop.Utility。
而ConfigConsole,调用PetShopConnectionString类EncryptConnectionString执行对联接字符串进行加密。另外PetShopEventLog类也是在ConfigConsole中使用的。用于记录程序日志。
所以在最后部署时Web.config的连接字符串是加密的。
7、PetShop.Model 业务实体模型
在SqlServerDAL和OracleDAL中都使用了Model
无论怎样,上层的程序执行最终结果都是要操作数据库,而数据库是关系型的,不是面向对象的,那就得把‘平面的’‘表’结合业务规则抽象成类(这里说的是将一张数据库表抽象为一个类,但是这里并没有继承DataTable类啊!!!),这样想办法让上层(BLL及以上)以为自已在操作类而不是数据库表,从而使‘它们’感觉没有数据库的存在,上层只管面向对象编程就可以了。
这类似现在所说的O-R MAPPING,但O-R MAPPING比这种简单的数据到对象的持久化要复杂。据说可以在表结构有变化的情况下,上层应用程序代码不用更改,只要改O-R MAPPING的相关设置就可以了。
上面第2节中已看到Petshop的SqlServerDal和OracleDal中定义的sql语句,然后根据上层的调用,把sql语句传给SqlHelper执行,再来看看SqlServerDal的一段程序:
private void SetAccountParameters(SqlParameter[] parms, AccountInfo acc)
{
parms[0].Value = acc.Email;
parms[1].Value = acc.Address.FirstName;
parms[2].Value = acc.Address.LastName;
parms[3].Value = acc.Address.Address1;
parms[4].Value = acc.Address.Address2;
parms[5].Value = acc.Address.City;
parms[6].Value = acc.Address.State;
parms[7].Value = acc.Address.Zip;
parms[8].Value = acc.Address.Country;
parms[9].Value = acc.Address.Phone;
parms[10].Value = acc.UserId;
}
注意:当在BLL中对Account实体进行OOP之后,某个Account对象的各个字段就存储了关于此Account的各个信息。而这些信息存储在这个Account对象中是无法更新到数据库当中去的!所以,要将此Account对象的各个字段“赋值”给一个SqlParameter[]数组,像本文第4页我写的,将此数组作为一个“做菜的原料”,然后使用DAAB直接“烹饪”了即可。“烹饪”之后,在BLL中对此Account对象进行的更改才会真正保存到数据库当中!!!!!!
parms[x]就是那些:声明为常量的 有参数的Sql语句中的 参数。
这里用Model中的AccountInfo的各Field和这些参数Mapping。所以对于BLL层,只知道这些Model就可以了(只对各Model的各字段赋值或操作就可以了),反正最后在SqlServerDAL或是OracleDAL中Model的成员们(既“字段们”)会Mapping到参数中以存取数据库(为什么不直接Mapping到数据集的字段呢?我想应该是因为这里数据库操作直接给SqlHeper的原因,不然就没必要用SqlHelper了。于是才又多了图中的xxx DAAB层,这样Mapping给参数再传给DAAB来处理)
Data Access Layer总结:
(1) DAL完成数据库访问任务,上层(BLL)直需调用接口即可,不用知道具体访问细节
(2) 用Factory模式实现,使用在运行时读取web.config的方法来得到连接配置信息
(3) 选用SqlServerDAL或OracleDAL之一的相对具体子层,同时使用SqlHelper或OraHelper之一来完成数据库操作
相关文章推荐
- PetShop3.x学习笔记5-我读cocoboy79《MS PetShop 3.x 设计与实现——数据访问层》
- PetShop3.x学习笔记5-我读cocoboy79《MS PetShop 3.x 设计与实现——数据访问层》
- PetShop3.x学习笔记5-我读cocoboy79《MS PetShop 3.x 设计与实现——数据访问层》
- PetShop3.x学习笔记5-我读cocoboy79《MS PetShop 3.x 设计与实现——数据访问层》
- PetShop3.x学习笔记10-购物车参考资料2-PetShop购物车实现
- PetShop3.x学习笔记10-购物车参考资料2-PetShop购物车实现
- Microsoft PetShop 3.0 设计与实现 分析报告―――数据访问层
- Microsoft PetShop 3.0 设计与实现--数据访问层
- Microsoft PetShop 3.0 设计与实现 分析报告---数据访问层
- Microsoft PetShop 3.0 设计与实现 分析报告―――数据访问层
- PetShop3.x学习笔记8-《PetShop 3.x的设计模式与体系结构》节选-数据库可移植性
- PetShop3.x学习笔记8-《PetShop 3.x的设计模式与体系结构》节选-数据库可移植性
- Microsoft PetShop 3.0 设计与实现 分析报告―――数据访问层
- Microsoft PetShop 3.0 设计与实现--数据访问层
- Microsoft PetShop 3.0 设计与实现--数据访问层
- Microsoft PetShop 3.0设计与实现—数据访问层
- Linux内核设计与实现 学习笔记(4)块I/O层
- Linux内核设计与实现 学习笔记(8)内核调试
- 学习笔记-Redis设计与实现-链表
- Linux中断(Linux内核设计与实现学习笔记)