控件中国网现已改版,您看到的是老版本网站的镜像,系统正在为您跳转到新网站首页,请稍候.......
中国最专业的商业控件资讯网产品咨询电话:023-67870900 023-67871946
产品咨询EMAIL:SALES@COMPONENTCN.COM

ASP.NET3.5 企业级项目开发 -- 第二章(续) 数据访问层(DAL)的开发解决方案提出

作者:小洋 出处:博客园 2010年02月03日 阅读:

前言:首先给大家说声"对不起",因为自从打算写这系列的文章以来,得到大家很多的支持,谢谢大家!最近因为公司的事和朋友找工作的事,没有怎么接着写了,也调了大家的胃口,还希望园子里的朋友原谅!

       本篇主要是讲述数据层的开发,之前的一篇文章已经给出了很多的选中的方案,如SqlHelper,DataTable/DataSet,以及自定义实体。但是我们说过了,那些方案都有不尽人意的地方,所以我们就提出用Linq,找个方案就比之前好一些,但是也不是那么完美了。
 
       本篇的话题如下:
       Linq中自定义方法
       提出解决方案

       Linq中自定义方法

       在此之前,我们先要讲清楚一个问题:在Linq中的自定义存储过程。
       我们知道,当我们把数据库中的一张表拖放到Linq的ORM设计器上以后,ORM就自动生成了很多的方法,如Delete,Update,Insert方法,这些方法都是自动生成的,一般是没有什么问题的,但是一个项目的开发中,很多时候我们都要把这些方法进行定制。那么我们就要用自己的存储过程。

       一般情况下,我们可以在我们的数据库中定义一些的存储过程,如下,我们在之前定义的ENTUserAccount表中添加一条数据:
 


CREATE PROCEDURE ENTUserAccountInsert
 (
  
@WindowsAccountName varchar(50),
  
@FirstName varchar(50),
  
@LastName varchar(50),
  
@Email varchar(100),
  
@IsActive bit,
  
@InsertENTUserAccountId int
 )
 
AS
 
SET NOCOUNT ON
 
INSERT INTO ENTUserAccount(WindowsAccountName, FirstName, LastName,
  Email, IsActive, InsertENTUserAccountId, InsertDate,
   UpdateENTUserAccountId, UpdateDate)
 
VALUES (@WindowsAccountName@FirstName@LastName@Email,
 
@IsActive@InsertENTUserAccountIdGetDate(),
  
@InsertENTUserAccountIdGetDate())
 
RETURN

 
       我们接来进行下面的操作;
       1.把这个存储过程拖放到ORM设计器的右边的方法面板中,这样存储过程就自动的生成一个方法。
       2.在ORM中点击ENTUserAccount表,选择属性中的Insert,如图:(插图出现问题,大家原谅!!!)
 
       3.在出现的面板中,我们选择Customer(自定义)。选择我们之前由存储过程实生成的的方法,确认就OK了。

       如果我们在我们的以后的代码中调用HRPaidTimeOffDataContext.InsertENTUseAccount方法,那么此时我们就实际上调用我们自定义的方法了,之前自动生成的Insert方法就不调用了。
 
       同理,Update,Delete方法的自定义也是一样的。
 
       提出解决方案

       约定:我们以后的存储过程的命名采用方式:表名+操作,如ENTUserAccountInsert。

       首先我们创建一下存储过程:

       ENTUserAccount表中插入数据


 


CREATE PROCEDURE [dbo].[ENTUserAccountInsert]
 (
  
@ENTUserAccountId int OUTPUT,
  
@WindowsAccountName varchar(50),
  
@FirstName varchar(50),
  
@LastName varchar(50),
  
@Email varchar(100),
  
@IsActive bit,
  
@InsertENTUserAccountId int
 )
 
AS
 
SET NOCOUNT ON
  
INSERT INTO ENTUserAccount (WindowsAccountName, FirstName,
   LastName, Email, IsActive, InsertDate,
   InsertENTUserAccountId, UpdateDate,
   UpdateENTUserAccountId)
  
VALUES (@WindowsAccountName@FirstName@LastName,
   
@Email@IsActiveGetDate(),
   
@InsertENTUserAccountIdGetDate(),
   
@InsertENTUserAccountId)

  
SET @ENTUserAccountId = Scope_Identity()
  
RETURN

 

 

       ENTUserAccount表中更新数据

 

 


CREATE PROCEDURE [dbo].[ENTUserAccountUpdate]
 (
  
@ENTUserAccountId int,
  
@WindowsAccountName varchar(50),
  
@FirstName varchar(50),
  
@LastName varchar(50),
  
@Email varchar(100),
  
@IsActive bit,
  
@UpdateENTUserAccountId int,
  
@Version timestamp
 )
 
AS
 
SET NOCOUNT ON
 
UPDATE ENTUserAccount
 
SET WindowsAccountName = @WindowsAccountName
  , FirstName 
= @FirstName
  , LastName 
= @LastName
  , Email 
= @Email
  , IsActive 
= @IsActive
  , UpdateDate 
= GetDate()
  , UpdateENTUserAccountId 
= @UpdateENTUserAccountId
 
WHERE ENTUserAccountId = @ENTUserAccountId
 
AND Version = @Version
 
RETURN @@ROWCOUNT

 ENTUserAccount表中删除数据
 
 
CREATE PROCEDURE [dbo].[ENTUserAccountDelete]
 (
  
@ENTUserAccountId int
 )
 
AS
 
SET NOCOUNT ON
 
DELETE FROM ENTUserAccount
  
WHERE ENTUserAccountId = @ENTUserAccountId
 
RETURN

 

 

        获取ENTUserAccount表中所有记录

 

 


 CREATE PROCEDURE ENTUserAccountSelectAll
 
AS
 
SET NOCOUNT ON
 
SELECT ENTUserAccountId, WindowsAccountName, FirstName, LastName,
  Email, IsActive, InsertDate, InsertENTUserAccountid,
  UpdateDate, UpdateENTUserAccountId, Version
 
FROM ENTUserAccount
 
RETURN

 

       通过ENTUserAccountId来查找记录
 
 


CREATE PROCEDURE ENTUserAccountSelectById
 (
  
@ENTUserAccountId int
 )
 
AS
 
SET NOCOUNT ON
 
SELECT ENTUserAccountId, WindowsAccountName, FirstName, LastName,
  Email, IsActive, InsertDate, InsertENTUserAccountid,
  UpdateDate, UpdateENTUserAccountId, Version
  
FROM ENTUserAccount
  
WHERE ENTUserAccountId = @ENTUserAccountId
 
RETURN

 

        以前的存储过程没有和大家以前做的项目一样,是很普通的一些存储过程。
       写完上面的存储过程之后,大家把Insert,Update,Delete的存储过程拖到ORM的方法面板中生成方法,然后按照我们之前说的方法定制,把ENTUserAccount的Update,Insert等方法都配置成为我们自己写的方法。


       注意:对于Select的存储过程,我们不应该把它们直接拖到方法面板中,我们而是把ENTUserAccountSelectAll,ENTUserAccountSelectById存储过程拖到到ORM设计器中的ENTUserAccount表上。因为只有这样,自定生成的代码才返回ENTUserAccount类的类型;如果是像之前那样拖到方法面板中,那么返回值就是ISingle<ENTUserAccount>,大家之后会看到原因的。
 
       之前,我们就在V2.PaidTimeOffDAL中创建一个文件夹,命名为Framework。让后在这个文件夹下面添加一个类,命名为IENTBaseEntity.cs

 


 namespace V2.PaidTimeOffDAL.Framework
 {
  
public interface IENTBaseEntity
  {
   DateTime InsertDate { 
getset; }
   
int InsertENTUserAccountId { getset; }
   DateTime UpdateDate { 
getset; }
   
int UpdateENTUserAccountId { getset; }
   Binary Version { 
getset; }
  }
 }

 

       因为Binary 类型是在System.Data.Linq下的,所以我们在类中添加这个命名空间的引用。
 
       大家在想:为什么我们要定义这么一个接口?
 
       原因有两点:
       1.因为我们的这个项目中之后会有审计跟踪的功能,就是说数据库中记录的每次修改都会记录下是谁在什么时候修改的,而且我们的所有的表中都会有上面的5个字段。定义接口这样做为了使得每个类都必需定义必需有上面的5个字段。


       2.定义接口,可以实现一些依赖倒置的原则,以后大家会看到效果的。

 
       添加完上面的类之后,那么我们就再添加一个类:CustomizedEntities.cs ,这个类不在Framework文件夹中,而是直接加在外面的。

 

 using V2.PaidTimeOffDAL.Framework;
 
namespace V2.PaidTimeOffDAL
 {
  
public partial class ENTUserAccount : IENTBaseEntity { }
 }

 

       大家可能会想:ENTUserAccount类是个partial类,那么就说明这个类之前已经在什么地方有了的?
       不错!我们之前说要把ENTUserAccountById的存储过程拖到ENTUserAccount表上,其实我们就是想生成这个和数据表同名的ENTUserAccount类。
 我们使得这个类实现这个接口,那么我们以后就可以"针对接口编程"。


       其实我们知道我们的DAL数据层的功能很明了:和数据库直接打交道,实现增,删,改,查操作。
 
       为了使得我们所有的数据操作统一,我们定义了一个基类,以后的所有数据库操作类都实现它。
       我们在Framework文件夹中添加类:ENTBaseData.cs
 
 


public abstract class ENTBaseData<T> where T : IENTBaseEntity
 {
  
public abstract List<T> Select();

  
public abstract T Select(int id);

  
public abstract void Delete(HRPaidTimeOffDataContext db, int id);

  
public void Delete(string connectionString, int id)
  {
   
using (HRPaidTimeOffDataContext db = new
    HRPaidTimeOffDataContext(connectionString))
   {
    Delete(db, id);
   }
  }
 }

 


 

      类的编写也不难理解:就是有写操作数据的方法。大家还要注意一点:Delete方法的实现,我们采用了Template Method设计模式,也就是常说的"钩子方法",实现的原因,现在讲述不是很好理解,没有一种自然的过程,我以后会说的。
 
       这样我们就定义这样的一个基类,然后针对每个表都有数据操作,那么我们就让每个表的数据操作类都继承这个基类。
       注意:大家在上面的基类中,没有看到Insert和Update方法,只是有原因的。如果我们在基类中写了Insert方法,就会传入一个T参数,如下:
     

  public bool Insert(T entUserAccount)

 
       这样是没有错,但是不是很好。如果我们的entUseAccount中如果有写字段没有赋值,那么我们插入就出问题。
       所以我们的实现方法是:直接在不同的数据操作类中,针对特定的表写Insert和Update方法。
 
       我们就来看看ENTUserAccount数据表的操作类的实现:


 


public class ENTUserAccountData : ENTBaseData<ENTUserAccount>
     {
        

        
public override List<ENTUserAccount> Select()
        {
            
using (HRPaidTimeOffDataContext db = new HRPaidTimeOffDataContext(DBHelper.GetHRPaidTimeOffConnectionString()))
            {
                
return db.ENTUserAccountSelectAll().ToList();                
            }
        }

        
public override ENTUserAccount Select(int id)
        {
            
using (HRPaidTimeOffDataContext db = new HRPaidTimeOffDataContext(DBHelper.GetHRPaidTimeOffConnectionString()))
            {
                
return Select(db, id);
            }
        }

  

       上面方法很简单:直接调用自动生成的方法。
 
       至于DBHelper.GetHRPaidTimeOffConnectionString()方法,就是把数据库的链接字符串从web.config文件中读出。
 
       下面我们就来看看Delete方法的实现:

 

  public override void Delete(HRPaidTimeOffDataContext db, int id)
        {
            db.ENTUserAccountDelete(id);
        }

 

       我们之前说过,只是一个Template Method模式的使用,使用的意图是:把具体实现延迟到子类。可能在以后的项目中,我们的数据库要更改了,表也改了,那时,我们的 ENTUserAccount数据表可能改为User表,那么我们之前实现的代码不变,只要重写Delete方法就行了,如
 

 public override void Delete(HRPaidTimeOffDataContext db, int id)
        {
            db. User Delete(id);
        }
 

 
       下面,我们就来看看Insert方法:

       其实实现也很简单,我们采用和SqlHelper相同的方式:第一个方法传入HRPaidTimeOffDataContext ,第二个方法传入connectionString
 
       而且我们传入的参数是和ENTUserAccount表相对应的字段,这样就比只是传入一个ENTUserAccount类要好。

 


public int Insert(string connectionString, string windowsAccountName, string firstName,
            
string lastName, string email, bool isActive, int insertUserAccountId)
        {
            
using (HRPaidTimeOffDataContext db = new HRPaidTimeOffDataContext(connectionString))
            {
                
return Insert(db, windowsAccountName, firstName, lastName, email, isActive, insertUserAccountId);
            }
        }


        
public int Insert(HRPaidTimeOffDataContext db, string windowsAccountName, string firstName, 
            
string lastName, string email, bool isActive, int insertUserAccountId)
        {
            Nullable
<int> entUserAccountId = 0;

            db.ENTUserAccountInsert(
ref entUserAccountId, windowsAccountName, firstName, lastName, 
                email, isActive, insertUserAccountId);

            
return Convert.ToInt32(entUserAccountId);
        }


        同理,Update方法实现如下;
 

 


 public bool Update(string connectionString, int userAccountId, string windowsAccountName,
            
string firstName, string lastName, string email, bool isActive, int updateUserAccountId,
            Binary version)
        {
            
using (HRPaidTimeOffDataContext db = new HRPaidTimeOffDataContext(connectionString))
            {
                
return Update(db, userAccountId, windowsAccountName, firstName, lastName, email,
                    isActive, updateUserAccountId, version);
            }
        }

        
public bool Update(HRPaidTimeOffDataContext db, int userAccountId, string windowsAccountName, 
            
string firstName, string lastName, string email, bool isActive, int updateUserAccountId, 
            Binary version)
        {
            
int rowsAffected = db.ENTUserAccountUpdate(userAccountId, windowsAccountName, firstName, 
                lastName, email, isActive, updateUserAccountId, version);
            
return rowsAffected == 1;
        }

 
       

      到这里,我们的DAL的实现的示例就到这里,当然,我们这里只有一个表,但是我们通过这个示例知道了我们之后实现方法,其他表的实现数据操作的方式一样的,随着深入的讲述,大家会看到的!

   为了大家交流,已经创建企业项目开发团队,希望大家也以后会把有关企业开发的文章放入团队中,希望大家积极参加这个团队。而且我以后也会发表更多的项目示例,大家一起学习进步!

热推产品

  • ActiveReport... 强大的.NET报表设计、浏览、打印、转换控件,可以同时用于WindowsForms谀坔攀戀Forms平台下......
  • AnyChart AnyChart使你可以创建出绚丽的交互式的Flash和HTML5的图表和仪表控件。可以用于仪表盘的创......
首页 | 新闻中心 | 产品中心 | 技术文档 | 友情连接 | 关于磐岩 | 技术支持中心 | 联系我们 | 帮助中心 Copyright-2006 ComponentCN.com all rights reserved.重庆磐岩科技有限公司(控件中国网) 版权所有 电话:023 - 67870900 传真:023 - 67870270 产品咨询:sales@componentcn.com 渝ICP备12000264号 法律顾问:元炳律师事务所 重庆市江北区塔坪36号维丰创意绿苑A座28-5 邮编:400020
在线客服
在线客服系统
在线客服
在线客服系统