ASP.NET3.5 企业级项目开发 -- 第二章 数据访问层(DAL)的开发

为什么80%的码农都做不了架构师?>>>   hot3.png

                                    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方法 ,其他的方法的设计思想和方式一样的:
 

 

09130459_UPGk.gif09130459_WT9v.gifCode
//连接字符串
 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)
 { … }

 

 

 

 

09130459_UPGk.gif09130459_WT9v.gifCode
 //连接对象
 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)
 { … }

 


 

09130459_UPGk.gif09130459_WT9v.gifCode
 //事务对象
 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个方法采用了一个事务对象为参数。这些方法在调用存储过程时提供了很大的灵活性。 例如:


 

09130459_UPGk.gif09130459_WT9v.gifCode
 CREATE PROCEDURE UserAccountInsert(
  
@Name varchar(50),
  
@UserAccountId int OUTPUT
 )
 
AS
 
SET NOCOUNT ON
  
INSERT INTO UserAccount (Name) VALUES (@Name)
 
SET @UserAccountId = Scope_Identity()

  
 调用代码如下:


 

09130459_UPGk.gif09130459_WT9v.gifCode
 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语句如下:



 

09130459_UPGk.gif09130459_WT9v.gifCode
 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#代码如下:
 


09130459_UPGk.gif09130459_WT9v.gifCode
 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 = 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)",服务器为".\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按钮点击事件下,添加下面代码:
 
 

09130459_UPGk.gif09130459_WT9v.gifCode
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.varallo@v2.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为btnUpdate,Text为Update,按钮事件代码如下:
 

 

09130459_UPGk.gif09130459_WT9v.gifCode
 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程序来说用,我们通常从数据库中获取一条数据,显示在界面上,然后用户更改数据,然后点击按钮回传到服务器,那么数据就传到了我们的DAL,DAL取执行更新操作。例如,我们把一个用户的信息全部取出了,包含FirstName,LastName,Email,IsActive等等,那么我们的界面上面就显示出来这些数据,我们假设是用TextBox来显示的,那么当我们更改了其中一个数据,如IsActive,那么我们就点击按钮,我们此时其实就要把界面上面的所有信息,包括FirstName,LastName,WindowAccountName,Email等全部返回到服务器(大家想想我们的一般更新的存储过程是怎样写的,就明白了),因为如果我们只是返回一个IsActive,服务器端根本不知道更新那条数据,此时我们在服务器端就应该写下面的代码:
 
  

09130459_UPGk.gif09130459_WT9v.gifCode


 
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.varallo@v2.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保存:


09130459_UPGk.gif09130459_WT9v.gifCode
 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按钮事件中,如下:


 

09130459_UPGk.gif09130459_WT9v.gifCode
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.varallo@v2.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了。如果大家有什么问题,就留言!

       删除记录:
 

09130459_UPGk.gif09130459_WT9v.gifCode
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是自动生成的。我们也可以定制。我们后面谈。

       查询数据:
       我们可以根据条件查询,然后绑定:


 

09130459_UPGk.gif09130459_WT9v.gifCode
 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/295927.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

22张图片倒叙霍金:我爱宇宙也爱这苦乐人生

全世界只有3.14 % 的人关注了爆炸吧知识▲斯蒂芬威廉霍金(Stephen William Hawking),1942年1月8日出生于英国牛津,21岁时患上肌肉萎缩性侧索硬化症(卢伽雷氏症),全身瘫痪,不能言语&a…

简述LINQ的发展历程

LINQ:最终统治了所有的语言!让我们看看LINQ如何彻底改变了.NET中访问数据的方式.NET与其他技术栈的不同之处之一绝对是LINQ,它是Language Integrated Query的首字母缩写。实际上,它是随.NET Framework 3.5和Visual Studio 2008引入…

PHP开发学习-Apache+PHP+MySQL环境搭建

我现在开始php的学习之旅啦! 入门:开发环境搭建 组件版本: apache2.2.22 下载地址:http://httpd.apache.org/ php5.4.11 下载地址:http://windows.php.net/download/ mysql5.5.29 下载地址:http://www.mys…

如何不露声色地知道别人是不是单身?

1 世界上竟然会有这么香的东西!!!2 全网吧的电脑都死机了,唯独写论文的电脑没死机!3 哈哈哈哈哈哈哈哈哈哈哈好拉风啊!!4 中国好邻居5 《秋游》,五年级男生写的诗图自我们1班王悦微6…

再谈C#中的委托和事件

写在最前我相信全网关于委托和事件的文章和概述,大家应该已经读过很多篇。但是就我的观察来看,大多数文在讲述这方面概念时,都会用烧开水和狗叫主人的例子来讲述事件怎么工作,这样比喻固然与生活联系紧密,但看多了难免…

js.domReady

2019独角兽企业重金招聘Python工程师标准>>> var dom [], dom_isReady false, domReady function (a) { if (dom_isReady) a(); else dom.push(a) }, dom_fireReady function () { if (!dom_isReady) { if (!document.body) return setTimeout(dom_fireReady, 1…

Android之监测database的改变--notifyChange

我们在ContentProvider的insert,update,delete等改变之后调用getContext().getContentResolver().notifyChange(uri, null);这样就通知那些监测databases变化的observer了,而你的observer可以在一个service里面注册。 以Downloadmanger为例子: 定义Cont…

双一流高校出新规:研究生未经导师同意发论文,不得用于毕业、评奖!

全世界只有3.14 % 的人关注了爆炸吧知识本文转自:募格学术近日,吉林大学研究生院发布文件《吉林大学关于加强对研究生在学期间公开发表论文等学术成果管理的通知》,因内容涉及校内研究生论文发表要求,引起很多研究生的注意。其中包…

android http 三次 握手,面试解析:3次握手与4次挥手

在面试中,三次握手和四次挥手可以说是问的最频繁的一个知识点了,我相信大家也都看过很多关于三次握手与四次挥手的文章。今天的这篇文章,重点是围绕着面试,我们应该掌握哪些比较重要的点,哪些是比较多被面试官给问到的…

微服务组件记事本:Skywalking执行效果 · 多图篇

立冬时节知多少今天立冬,周末两天在家继续研究了下Skywalking,感觉这个组件还是很不错的,无论是设计思想还是架构设计,都能从中受到启发和帮助,建议感兴趣的小伙伴可以看看,当然,如果不感兴趣还…

堆和栈的区别 (转贴)

从其他博客复制过来的,因为这个够详细,转过来学习一下! 堆和栈的区别一、预备知识—程序的内存分配一个由c/C编译的程序占用的内存分为以下几个部分1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数…

ios和android交互差异,Android 和 iOS 主要交互区别整理

我简单整理了一下Android 和 iOS 主要的交互区别,如果有遗漏欢迎批评补充。我总结有下面五类区别:一、界面布局形式的差异1 iOS 的 Tab Bar在iOS应用内最多用Tab Bar来切换不同的模块,Tab Bar 也是最受欢迎最容易被用户认知的方式。2 Android…

要比惨吗?看看这个女人

全世界只有3.14 % 的人关注了爆炸吧知识她从不知道辞职是什么滋味,因为被炒的总是她;连续被三家公司炒掉之后,她不得不自己做SOHO。生了双胞胎,本是喜事;但还在月子里,却发现老公出轨。小三还没打走&#x…

【Blog.Core开源】完成升级.NET 6.0

(千呼万唤始出来,_ _ _ _ _ _ _)是不是每个人都已经尝试一遍vs2022和.NET6.0了😁,从各个微信群和盆友圈就可见一斑。今天一大早,一位粉丝就发私信,让升级6.0,这么简单的需求,必须满足&#xff0…

Jenkins file一行代码部署.NET程序到K8S

Jenkins file一行代码部署.NET程序什么是Jenkins共享库随着微服务的增多,每个项目的都需要pipline文件,这样的话Pipeline代码冗余度高,并且pipeline的功能越来越复杂。jenkins可以使用Shared Lib,将一些公共的pipeline抽象做成模块…

吵架后女生和男生的夜晚!所有男生都这样吗?

1 猫:哪个瓜娃子开的灯?!2 神回复在哪里3 舔舔舔舔舔,被发现了!4 喵喵:你当我傻吗?5 今晚可不可以到你家吃饭6 吵架后女生的夜晚vs吵架后男生的夜晚太真实了7 打游戏时候 对方队友和我方…

项目背景介绍

Adventure 项目(1) 墨翟坐在办公室里,正和秘书 Alan 开会,讨论着一件让他和 Alan 都很烦心的事情。Adventure 在全球都有工厂,制造各种不同的产品。作为制造部门的负责人,墨翟需要每个月了解各个工厂的运行…

不想升Win11?Win10新版马上到!

微软正式宣布 Windows 10 操作系统的下一个功能更新:Windows 10 版本 21H2。微软表示:虽然我们对下一代 Windows 11 感到兴奋,但也专注于支持 Windows 10 上超过 13 亿台活跃设备。当人们继续在混合和远程环境中工作、学习和娱乐时&#xff0…

娱乐项目和女朋友哪个重要?

1 这招风卷残云我只演示一遍,看好了2 摄影师给男生拍照VS给女生拍照3 这个视力表是永远不会近视的4 娱乐项目和女朋友哪个重要?小伙子你飘了啊5 最怕空气突然安静6 “灵魂六问”7 原来是这样周末愉快↓ ↓ ↓

这些高校竟因名字太“坑爹”被误会为三本?盘点九所实力强劲但被名字耽误的大学...

全世界只有3.14 % 的人关注了爆炸吧知识有这样一个段子:西北某地一学生,志存高远,矢志从医。首次高考考入泸州医学院,到校后发现不理想,果断返乡复读。一年后成功考入四川医科大学,到校后傻眼了&#xff0c…