最新澳门网站网址游戏ASP.NET3.5 企业级项目开发

最新澳门网站网址游戏 1

                                    ASP.NET3.5 企业级项目开发 -- 第二章 数据访问层(DAL)的开发
 
       前言:本篇主要讲述数据访问层的开发,而且为了大家交流,已经创建企业项目开发团队,希望大家也以后会把有关企业开发的文章放入团队中,希望大家积极参加这个团队。而且我以后也会发表更多的项目示例,大家一起学习进步!
 
       本篇的话题主要如下:

开发平台是Winform,c#+Sql2008。在Sql中设置了非自增整型主键"ID"。功能要求:通过DataTable读取Sql数据,在界面上使用DataGridView控件进行显示,在界面上增加修改删除数据,并更新至Sql中。另外,"ID"除了主键以外,在界面上作为顺序号显示,所以每次操作后,我要对"ID"进行排序。目前,大部分功能已实现并运行正常,只有在新增一行数据并插入DataTable任意位置中,更新数据库时会报"违反了PRIMARYKEY约束'PK__CFG_Basi__3214EC276C190EBB'。不能在对象'dbo.CFG_Basic_Table'中插入重复键。"错误。如果插入的行在DataTable末尾则不会异常。期间,曾测试在DataTable中修改"ID"数值,依然无法解决问题。更新数据至数据库使用的是SqlDataAdapter.Update()函数。请指点,多谢。

       问题提出
       设计方案
 
       问题提出
 
       数据访问层(DAL)的目标创建一些以便业务层来调用的类和方法。我们之前总是用GridView来绑定DataSet和DataReader,但是在稍微大点的项目开发中DAL不能直接和用户

privatevoidForm1_Load(objectsender,EventArgse){//加载Sql数据至界面dataGridViewX1strTableName="CFG_Basic_BanknoteAttributeTable";//SqlConn=newSqlConnection();SqlConn.ConnectionString="server=127.0.0.1;database=XC_MIS_DB_11;uid=sa;pwd=1111;ConnectionTimeout=10";SqlConn.Open();//sda=newSqlDataAdapter();dsSettings=newDataSet();//sda=newSqlDataAdapter("Select*From"+strTableName,SqlConn);sda.Fill(dsSettings,strTableName);//dataGridViewX1.DataSource=dsSettings;dataGridViewX1.DataMember=strTableName;//}privatevoidbtnAdd_Click(objectsender,EventArgse){//新增一行DataRowdr=dsSettings.Tables[strTableName].NewRow();//将第一行数据赋值予新增行dr.ItemArray=dsSettings.Tables[strTableName].Rows[1].ItemArray;//新增行插入第0行dsSettings.Tables[strTableName].Rows.InsertAt(dr,0);//ID排序//DgvIDSort(dsSettings.Tables[strTableName]);}privatevoidbtnDelete_Click(objectsender,EventArgse){//删除行dsSettings.Tables[strTableName].DefaultView[dataGridViewX1.CurrentRow.Index].Delete();}privatevoidbtnSave_Click(objectsender,EventArgse){//更新至数据库SqlCommandBuilderscb=newSqlCommandBuilder(sda);sda.Update(dsSettings.Tables[strTableName]);//DataRow[]d=newDataRow[dsSettings.Tables[strTableName].Rows.Count];//for(inti=0;idsSettings.Tables[strTableName].Rows.Count;i++)//{//d[i]=dsSettings.Tables[strTableName].Rows[i];//}//}

      界面打交道。
      
       一般来说,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方法 ,其他的方法的设计思想和方式一样的:  

 

最新澳门网站网址游戏 2最新澳门网站网址游戏 3Code
//连接字符串
 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)
 { … }

 

 

 

 

最新澳门网站网址游戏 4最新澳门网站网址游戏 5Code
 //连接对象
 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)
 { … }

 

 

最新澳门网站网址游戏 6最新澳门网站网址游戏 7Code
 //事务对象
 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个方法采用了一个事务对象为参数。这些方法在调用存储过程时提供了很大的灵活性。 例如:

 

最新澳门网站网址游戏 8最新澳门网站网址游戏 9Code
 CREATE PROCEDURE UserAccountInsert(
  @Name varchar(50),
  @UserAccountId int OUTPUT
 )
 AS
 SET NOCOUNT ON
  INSERT INTO UserAccount (Name) VALUES (@Name)
 SET @UserAccountId = Scope_Identity()

  
 调用代码如下:

 

最新澳门网站网址游戏 10最新澳门网站网址游戏 11Code
 public int InsertUserAccount(string connectionString, string name)
 {
  SqlParameter[] parameters =
  {
   new SqlParameter( "@Name", SqlDbType.VarChar, 50),
   new SqlParameter( "@UserAccountId", SqlDbType.Int)
  };
  parameters[0].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语句如下:

 

最新澳门网站网址游戏 12最新澳门网站网址游戏 13Code
 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#代码如下:  

最新澳门网站网址游戏 14最新澳门网站网址游戏 15Code
 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[0].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 Express创建也行,如下:    

最新澳门网站网址游戏 16

 

      

       2.数据库名称为HRPaidTimeOff
       3.创建ENTUserAccount表,如下:

最新澳门网站网址游戏 17

 

       我们在这个项目中的所有表都以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 = yangyang4502@yahoo.com.cn
       IsActive = True
       InsertDate=getdate()
       InsertENTUserAccountId = 1
       UpdateDate = getdate()
       UpdateENTUserAccountId = 1

 

       现在数据表就创建好了,打开VS2008创建连接(如果我们之前是直接用VS2008的服务器资源管理创建的,那么下面的步骤就不用作了,如果使用的 sql 2005,下面的步骤就要做

       1.在VS2008中选择"视图"-"服务器资源管理"
       2.点击"数据库连接",右击,"添加连接",数据库选择"Microsoft SQL Server (SqlClient)",服务器为".sqlexpress".
       3.选择身份验证方式为"Windows验证",选择数据库为"HRPaidTimeOff"
       4.测试连接,然后OK
 
       下面我们就来创建DataContext
       我们之前说过:DataContext和我们之前谈的SqlHelper类来类比,它们都是封装了数据操作的细节。我们在V2.PaidTimeOffDAL项目上右击,选择"添加新项",如下:  

最新澳门网站网址游戏 18

 
       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",就可以看到下面的方法:

本文由最新澳门网站网址游戏发布于产品展示,转载请注明出处:最新澳门网站网址游戏ASP.NET3.5 企业级项目开发

您可能还会对下面的文章感兴趣: