厦门亚龙网站建设社区网站建设公司
news/
2025/9/24 6:42:12/
文章来源:
厦门亚龙网站建设,社区网站建设公司,西安做网站哪家便宜,广东深圳市宝安区为什么80%的码农都做不了架构师#xff1f; ASP.NET3.5 企业级项目开发 #xff0d;#xff0d; 第二章 数据访问层(DAL)的开发 前言#xff1a;本篇主要讲述数据访问层的开发#xff0c;而且为了大家交流#xff0c;已经创建企业项目开发团队 ASP.NET3.5 企业级项目开发 第二章 数据访问层(DAL)的开发 前言本篇主要讲述数据访问层的开发而且为了大家交流已经创建企业项目开发团队希望大家也以后会把有关企业开发的文章放入团队中希望大家积极参加这个团队。而且我以后也会发表更多的项目示例大家一起学习进步 本篇的话题主要如下 问题提出 设计方案 问题提出 数据访问层(DAL)的目标创建一些以便业务层来调用的类和方法。我们之前总是用GridView来绑定DataSet和DataReader但是在稍微大点的项目开发中DAL不能直接和用户 界面打交道。 一般来说DAL是用来和数据库和BLL打交道的也就是处理BLL和数据库的中间。数据以什么形式在DAL和BLL之前传递有很多的争论。不同的人有不同的意见数据传递的形式有DataSet强类型的DataSet,DataReader,自定义实体。在介绍Ling to Sql之后大家心里会有清晰的答案。在以前的开发中我们一般是采用ADO.NET来和数据库打交道那么就需要我们的开发人员对ADO.NET有一定的比较深入的了解但是当我们用Linq to Sql之后我们可以很方便的使用DataContext来与数据库拉打交道而不需要我们懂得很多的ADO.NET的知识但是在Linq to Sql的背后还是在采用ADO.NET来和数据库交互的。 还有就是事务处理的问题。关于事务的概念相信大家都清楚我也不赘述了。事务处理在什么地方实现有如下意见在存储过程中直接用SQL语句来写在DAL层处理 在BLL层处理。当然每一种的选择都有各自的理由和利弊。还有一点要注意的是不要把事务处理的代码到处写如在DAL层中写一点在BLL中写一点。 设计方案 在设计方案中实际上就是提供几个选择来解决之前我们提出的问题。以下就是两个选择 1.DAL只要是执行CRUD操作CRUD是就是Create,Read,Update,Delete.在.NET Framwework中提供了很多和数据库打交道的ADO.NET类和方法如 SqlConnection,SqlCommand,SqlCommand.ExecuteNonQuery()等用过ADO.NET的朋友应该清楚这些常用的类我这里也不罗嗦。 2.SqlHelper 用过ADO.NET的朋友应该知道在我们开发过程中很多时候写ADO.NET代码的时候代码结构和功能都是大同小异的所以基于此微软就开发了Microsoft Data Access Application Block只要我们调用其中的一些方法传入一些参数就行了不需要我们再去写那些繁琐的ADO.NET代码因为这个数据访问块都已经封装好了。其中一个最重要的类就是SqlHelper.这个类是个静态类提供了很多的方法如下 ExecuteNonQuery ExecuteDataset ExecuteReader ExecuteScalar FillDataset 上面 方法的设计包含了很多OO的思想。我们来看看ExecuteNonQuery方法 其他的方法的设计思想和方式一样的 Code //连接字符串 public static int ExecuteNonQuery(string connectionString, CommandType commandType,string commandText) { … } public static int ExecuteNonQuery(string connectionString, CommandType commandType,string commandText, params SqlParameter[] commandParameters) { … } public static int ExecuteNonQuery(string connectionString, string spName, params object[] parameterValues) { … } Code //连接对象 public static int ExecuteNonQuery(SqlConnection connection, CommandType commandType, string commandText) { … } public static int ExecuteNonQuery(SqlConnection connection, CommandType commandType, string commandText, params SqlParameter[] commandParameters) { … } public static int ExecuteNonQuery(SqlConnection connection, string spName, params object[] parameterValues) { … } Code //事务对象 public static int ExecuteNonQuery(SqlTransaction transaction, CommandType commandType, string commandText) { … } public static int ExecuteNonQuery(SqlTransaction transaction, CommandType commandType, string commandText, params SqlParameter[] commandParameters) { … }public static int ExecuteNonQuery(SqlTransaction transaction, string spName, params object[] parameterValues) { … } 上面前3个方法都是采用了一个连接字符串的参数而接下来的3个方法是采用了一个连接对象为参数最后的3个方法采用了一个事务对象为参数。这些方法在调用存储过程时提供了很大的灵活性。 例如 Code CREATE PROCEDURE UserAccountInsert( Name varchar(50), UserAccountId int OUTPUT ) AS SET NOCOUNT ON INSERT INTO UserAccount (Name) VALUES (Name) SET UserAccountId Scope_Identity() 调用代码如下 Code public int InsertUserAccount(string connectionString, string name) { SqlParameter[] parameters { new SqlParameter( Name, SqlDbType.VarChar, 50), new SqlParameter( UserAccountId, SqlDbType.Int) }; parameters[].Value name; parameters[1].Direction ParameterDirection.Output; SqlHelper.ExecuteNonQuery(connectionString,CommandType.StoredProcedure, UserAccountInsert, parameters); return Convert.ToInt32(parameters[1].Value); } 还有一个问题要注意就是更新时的同步问题。例如用户从数据库中获取一条数据然后更改了一些内容之后就保存记录到数据库中那么系统就应该只保存用户之前取出的那条数据。为了达到这个效果我们可以在每次数据更新的时候添加一个datetime类型或者int类型的字段来标记。当我们从数据库中返回一条记录时我们在UI显示层显示出来然后修改数据再保存到数据库中那么我们在数据库中的存储过程的SQL语句就只是更新之前我们取出的那条数据。在Sql Server 2005中我们可以用timespan类型的字段来跟踪和标识每条数据的版本所以sql语句如下 Code CREATE PROCEDURE UserAccountUpdate( Name varchar(50), UserAccountId int, LastUpdateDate datetime ) AS UPDATE UserAccount SET Name Name, LastUpdateDate GetDate() WHERE UserAccountId UserAccountId AND LastUpdateDate LastUpdateDate 请大家注意LastUpdateDate 字段其实就是一个标识每条数据版本的辅助字段。 调用的C#代码如下: Code public bool UpdateUserAccount(string connectionString, string name, int userAccountId, DateTime lastUpdateDate) { SqlParameter[] parameters { new SqlParameter( Name, SqlDbType.VarChar, 50), new SqlParameter( UserAccountId, SqlDbType.Int), new SqlParameter( LastUpdateDate, SqlDbType.DateTime) }; parameters[].Value name; parameters[1].Value userAccountId; parameters[2].Value lastUpdateDate; int rowsAffected Convert.ToInt32(SqlHelper.ExecuteNonQuery(connectionString, CommandType.StoredProcedure, UserAccountUpdate, parameters)); return rowsAffected 1; } 下面我们就来看看数据在DAL和BLL之间是以什么形式来交换的一般有以下选择 DataSet 类型化的DataSet 自定义实体 当BLL类调用从DAL中的一些方法拉获取数据时它们将怎样接受这些数据 是用DataSet/DataTable,还是自定义实体 下面我们就看看给自的优缺点 如果选择使用DataSet/DataTable在DAL和BLL层之前传递数据就需要使用ADO.NET中的方法来访问BLL中的数据。如果选择自定义实体那么所有的数据将被封装到自定义实体类和类的集合中这样就可以根据具体情况来访问BLL这中方式更加的自然。 很多人认为DataSet/DataTable对于基于桌面的只能客户端程序来说是最好的选择但是对于可扩展的高性能Web网站来说则不够强大。这里所说的DataSet是指类型化的DataSet因为非类型化的DataSet有很多的确定进行配置和编码时很容易把表名字段名关系名或者字段的类型搞错而且在调试的时候花费很多的时间。而类型化的DataSet很容易使用因为它可以使用智能感应来获得字段名而且还内置了排序和过滤的功能执行数据绑定而且DataSet/DataTable还是可以序列化的这样就可以更加容易的传输它们。 DataSet/DataTable的缺点性能和扩展性的局限性数据的表示形式和业务规则验证。如果只需要传递一条数据那么我们仍然需要创建一个完整的DataSet/DataTable这就需要系统开销。而且DataSet/DataTable和数据库的关于很密切可以说是内存中的数据库所以DataSet/DataTable没有一个清晰的面向对象的数据表示方式。尽管DataSet/DataTable同IDE集成的很好但是每次数据库结构发生变化如添加字段重命名等那么我们就得重新创建类型化的DataSet.最大的问题就是在DataSet中添加自定义的业务和验证逻辑困难。在将它们保存到数据库之前或者在运行其他语句之前要对那些新记录强制之心业务规则需要写很多的代码而且需要深入的了解ADO.NET的知识。大家要明白 如果使用的是自定义的实体那么我们就需要手动的把数据库中的表映射为自定义的业务类而且这将是一项麻烦的事情而且还有一点很之前的DataSet/DataTable一样:如果数据库中的表结构变化那么我们又得重新修改我们的自定义的业务类 我们真的就没有办法了吗 有绝对有方法可以解决上面的问题那么就Linq to Sql 和它的ORM设计器。 下面我们就看看Linq to Sql 注在本章中不需要大家对Linq很熟悉只要了解就行了因为对与用到的知识会详细的讲述的。而且大家一定要一步步的跟着做切记 在VS2008中一个改进的功能就是Linq还有就是对象关系模型(ORM)设计器ORM设计器可以自动的生成类并且自动进行ADO.NET的操作。其中一个最中要的类就是DataContext,大家可以把这个类和我们之前谈的SqlHelper类来类比它们都是封装了数据操作的细节。 下面请大家跟着我一起来做 1.创建一个新的Sql Server 2005 数据库或者直接用VS2008自带的SQL Exss创建也行,如下 2.数据库名称为HRPaidTimeOff 3.创建ENTUserAccount表如下 我们在这个项目中的所有表都以ENT为前缀表明这个表是可重用企业级框架的一部分。还有ENTUserAccountId是identity的。 来看看表的定义和字段的意义这个表其实就是一个用户账户表其中ENTUserAccountId就是用户账户的ID也是主键WindowsAccountName就是用户的计算机名称FirstName,LastName就是用户名字Email用户邮箱IsActive表明这个用户是否是激活状态后面的IsertDate,InsertENTUserAccountId,UpdateDate,UpdateENTUserAccountId, 这几个字段在我们项目的所有的表中都有的因为我们之后要添加审计和跟踪功能要用到这些字段它们的意义分别是用户添加的时间是哪个用户添加了当前的这个用户用户更新时间是哪个用户更新的当前用户。举个例子就是加入Mary是管理员她添加了一个Bob用户那么ENTUserAccountId就是Bob用户记录的主键FirstName就是Bob,InsertENTUserAccountId就是Mary的ID。 下面我们就添加一条记录 WindowsAccountName Lufy FirstName Yang LastName Wang Email yangyang4502yahoo.com.cn IsActive True InsertDategetdate() InsertENTUserAccountId 1 UpdateDate getdate() UpdateENTUserAccountId 1 现在数据表就创建好了打开VS2008创建连接(如果我们之前是直接用VS2008的服务器资源管理创建的那么下面的步骤就不用作了如果使用的 sql 2005下面的步骤就要做 1.在VS2008中选择视图-服务器资源管理 2.点击数据库连接,右击,添加连接数据库选择Microsoft SQL Server (SqlClient),服务器为.\sqlexss. 3.选择身份验证方式为Windows验证,选择数据库为HRPaidTimeOff 4.测试连接然后OK 下面我们就来创建DataContext 我们之前说过DataContext和我们之前谈的SqlHelper类来类比它们都是封装了数据操作的细节。我们在V2.PaidTimeOffDAL项目上右击选择添加新项如下 VS2008自动的添加System.Data.Linq的引用同时也添加了三个文件HRPaidTimeOff.dbml, RPaidTimeOff.dbml.layout和HRPaidTimeOff.designer.cs.其中HRPaidTimeOff.dbml, RPaidTimeOff.dbml.layout将会被ORM图形设计器所使用.cs文件包含了所有自动创建的类。 大家可以双击HRPaidTimeOff.designer.cs文件我们就会看见VS2008创建的分部类HRPaidTimeOffDataContext这个类继承自System.Data.Linq.DataContext。我们可以用这个类来和数据打交道就想我们之前使用SqlHelper类一样。这个类有一个名为MappingSource的变量和一些构造函数。其中构造函数有的采用一个连接字符串为参数有的用一个实现了IDBConnection接口的类为参数然后所有的构造函数都调用了OnCreated方法。 打开折叠区域Extensibility Method Definitions,就可以看到下面的方法 partial void OnCreated(); 这是VS2008的一个新的语法分部方法大家可以和之前的partial class类比。这个方法允许你在类中先定义方法然后在该类的其他partial类中实现方法体。如果我们的partial 方法没有实现那么及时你调用了这个方法也不会出错因为编译器会忽略这个方法的调用。 下面我们双击HRPaidTimeOff.dbml就可以看到图形化的ORM设计器。然后我们展开服务器资源管理器窗口然后把PaidTimeOff数据库总的ENTUserAccount拖到设计器的左边。如下 此时VS2008就直接创建一个实体类来映射ENTUserAccount表再次打开HRPaidTimeOff.designer.cs文件我们可以看到这个文件和我们之前看到的就不同了我们首先可以看到下面的Attribute被添加了 [System.Data.Linq.Mapping.DatabaseAttribute(Name HRPaidTimeOff )] 而且在Extensibility Method Definitions:很多新的方法也添加了 partial void InsertENTUserAccount(ENTUserAccount instance); partial void UpdateENTUserAccount(ENTUserAccount instance); partial void DeleteENTUserAccount(ENTUserAccount instance); 而且一个新的构造函数也添加了这个函数采用一个连接字符串为参数和一个映射源为参数 public HRPaidTimeOffDataContext() : base ( global ::V2.PaidTimeOffDAL.Properties.Settings.DefaultHRPaidTimeOffConnectionString, mappingSource) { OnCreated(); } 除此之外还有一些类添加了在解决方案窗口我们可以看到Settings.settings文件和Settings.Designer.cs文件打开Settings.Designer.cs文件我们可以看到这个类是继承自global::System.Configuration.ApplicationSettingsBase有一个属性很重要就是HRPaidTimeOffConnectionString,这属性返回数据库连接字符串 所以在默认情况下我们之前建立的HRPaidTimeOffDataContext的数据库连接字符串在Settings文件中指定。 我们再回到HRPaidTimeOffDataContext类我们可以找到一个ENTUserAccount的类它就是数据库表的一个映射类。 我们只要了解这么多就行了下面就通过例子的使用来了解其他的知识。 添加数据记录 编译V2.PaidTimeOffDAL,我们在我们解决方案的那个网站项目这引用V2.PaidTimeOffDAL.dll,我们这里只是临时的使用以下V2.PaidTimeOffDAL.dll而以大家知道UI层不能直接和DAL打交道的我们这里暂时的使用使得大家对Linq更加的熟悉一点。 大家跟着做 1.在网站项目中添加System.Data.Linq引用。 2.在Default.aspx页面添加一个按钮ID为btnInsert,Text为Insert 3.在Default.aspx.cs添加V2.PaidTimeOffDAL引用。 4.在Insert按钮点击事件下添加下面代码 Code protected void btnInsert_Click(object sender, EventArgs e) { //创建DataContext实例 HRPaidTimeOffDataContext db new HRPaidTimeOffDataContext(); //创建新的ENTUserAccount 对象并且设计属性 ENTUserAccount userAccount new ENTUserAccount { WindowsAccountName VARALLO1\VaralloMadison, FirstName Madison, LastName Varallo, Email madison.varallov2.com, IsActive true, InsertDate DateTime.Now, InsertENTUserAccountId 1, UpdateDate DateTime.Now, UpdateENTUserAccountId 1 }; //传入数据 db.ENTUserAccounts.InsertOnSubmit(userAccount); //保存到数据库 db.SubmitChanges(); } 上面的代码其实和我们之前使用ADO.NET的形式很接近。首先我们创建一个连接实例HRPaidTimeOffDataContext和数据库连接然后就是创建一条数据然后调用InsertOnSubmit方法插入数据最后就保存数据db.SubmitChanges()把数据保存到数据库中。 更新数据 在Default.aspx页面中再添加一个按钮ID为btnUpdateText为Update按钮事件代码如下 Code protected void btnUpdate_Click(object sender, EventArgs e) { HRPaidTimeOffDataContext db new HRPaidTimeOffDataContext(); ENTUserAccount userAccount db.ENTUserAccounts.Single(u u.WindowsAccountName VARALLO1\VaralloMadison); userAccount.IsActive false; db.SubmitChanges(); } 这个段代码和之前的差不多首先还是实例化一个数据库连接的对象HRPaidTimeOffDataContext 然后用Lamdal表达式选出一条数据然后更改IsActive false最后保存回数据库中。 大家可以看到使用Linq以后我们再也没有用ADO.NET的语句了而且我们写代码的方式也完全改变。 我们从更新数据的例子中看到我们首先从数据库中获取一条数据然后改变数据最后再次保存回数据库。对于Web程序来说用我们通常从数据库中获取一条数据显示在界面上然后用户更改数据然后点击按钮回传到服务器那么数据就传到了我们的DALDAL取执行更新操作。例如我们把一个用户的信息全部取出了包含FirstName,LastName,Email,IsActive等等那么我们的界面上面就显示出来这些数据我们假设是用TextBox来显示的那么当我们更改了其中一个数据如IsActive那么我们就点击按钮我们此时其实就要把界面上面的所有信息包括FirstName,LastName,WindowAccountName,Email等全部返回到服务器(大家想想我们的一般更新的存储过程是怎样写的就明白了)因为如果我们只是返回一个IsActive服务器端根本不知道更新那条数据此时我们在服务器端就应该写下面的代码 Code protected void btnUpdate_Click(object sender, EventArgs e) { //Create an ENTUserAccount object and set the properties ENTUserAccount userAccount new ENTUserAccount { ENTUserAccountId 2, WindowsAccountName VARALLO1\VaralloMadison, FirstName Madison, LastName Varallo, Email madison.varallov2.com, IsActive false, UpdateDate DateTime.Now, UpdateENTUserAccountId 1 }; HRPaidTimeOffDataContext db new HRPaidTimeOffDataContext(); db.ENTUserAccounts.Attach(userAccount, true); db.SubmitChanges(); } 这段代码和之前一样也是把IsActive设置为false但是我们调用的是Attach方法而且把第二个参数设置为true说明我们要采用更新操作。 如果运行代码会报错的 An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy. 意思是说我们回传的那条记录在数据库中不存在。 这里还有一个问题要说明我们确实是从数据库中获取一条数据然后显示在界面上我们更新数据后数据要取数据库中更新那么数据库怎么知道现在我们传来的数据就是之前从数据库中取出的那条呢 所以我们就需要一个标识或者说是时间戳来表明我们现在回传的数据确实属于数据库那么我们就修改我们的数据表ENTUserAccount,添加一个新的字段Version,类型为timespan。所以当我们取数据的时候我们就把数据的版本Version字段保存在ViewState中然后更新数据之后我们把这个字段一起返回数据库就检查我们传回的Version字段是否和我们要更新的数据记录的Version是否相同如果相同就更新数据如下代码 Page_Load事件中把Version保存 Code protected void Page_Load(object sender, EventArgs e) { HRPaidTimeOffDataContext db new HRPaidTimeOffDataContext(); ENTUserAccount userAccount db.ENTUserAccounts.Single(ua ua.WindowsAccountName VARALLO1\VaralloMadison); ViewState[ENTUserAccountId] userAccount.ENTUserAccountId; ViewState[InsertENTUserAccountId] userAccount.InsertENTUserAccountId; ViewState[InsertDate] userAccount.InsertDate; ViewState[Version] userAccount.Version; } 在btnUpdate按钮事件中如下 Code protected void btnUpdate_Click(object sender, EventArgs e) { //Create an ENTUserAccount object and set the properties ENTUserAccount userAccount new ENTUserAccount { WindowsAccountName VARALLO1\VaralloMadison, FirstName Madison, LastName Varallo, Email madison.varallov2.com, IsActive false, UpdateDate DateTime.Now, UpdateENTUserAccountId 1 }; userAccount.ENTUserAccountId Convert.ToInt32(ViewState[ENTUserAccountId]); userAccount.InsertENTUserAccountId Convert.ToInt32(ViewState[InsertENTUserAccountId]); userAccount.InsertDate Convert.ToDateTime(ViewState[InsertDate]); userAccount.Version (Binary)ViewState[Version]; HRPaidTimeOffDataContext db new HRPaidTimeOffDataContext(); db.ENTUserAccounts.Attach(userAccount, true); db.SubmitChanges(); } 这样就OK了。如果大家有什么问题就留言 删除记录 Code protected void btnDelete_Click(object sender, EventArgs e) { HRPaidTimeOffDataContext db new HRPaidTimeOffDataContext(); //Create an ENTUserAccount object ENTUserAccount userAccount new ENTUserAccount(); userAccount.ENTUserAccountId Convert.ToInt32(ViewState[ENTUserAccountId]); userAccount.Version (Binary)ViewState[Version]; db.ENTUserAccounts.Attach(userAccount); db.ENTUserAccounts.DeleteOnSubmit(userAccount); db.SubmitChanges(); } 方法DeleteOnSubmit是自动生成的。我们也可以定制。我们后面谈。 查询数据 我们可以根据条件查询然后绑定 Code protected void btnSelect_Click(object sender, EventArgs e) { HRPaidTimeOffDataContext db new HRPaidTimeOffDataContext(); var userAccounts from u in db.ENTUserAccounts select u; GridView1.DataSource userAccounts; GridView1.DataBind(); } 今天我们就说到这里谈了一些方法和预备的方案我们下篇就说说在我们这个项目DAL到底如何设计。 为了大家交流已经创建企业项目开发团队希望大家也以后会把有关企业开发的文章放入团队中希望大家积极参加这个团队。而且我以后也会发表更多的项目示例大家一起学习进步 原文链接 http://www.cnblogs.com/yanyangtian/archive/2009/05/31/1493014.html 转载于:https://my.oschina.net/dtec/blog/43579
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/915105.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!