晒晒我的通用数据访问层

运维 数据库运维
笔者在从事多年数据库项目编写之后,厌倦了机械化的代码,不断重构出了属于自己的代码——自己的通用数据访问层。在这里与大家分享。

今天来晒晒我的通用数据访问层。

写了很多年的数据库项目,数据访问嘛,一直是用业务实体+存储过程的方式,因此经常会写很多调用存储过程的代码。这些代码用Ado.net如何写,我想大家应该都知道:创建Connection, 创建Command, 给命令参数一个一个赋值,然后调用,调用完成后,如果有输出参数,则要读出来,如果有结果集,则要将结果集转换成自己的实体列表,这个过程也是非常机械化的。总之,调用任何存储过程都需要这样一堆类似的代码。

我是个喜欢最求完美的人,自然不喜欢每个项目都有这样一堆机械代码的存在,于是经过不断的重构代码,慢慢的就形成了自己的通用数据访问层。

我的通用数据访问层具有以下特点

  1. 可用于访问各种类型的数据库,让您的应用程序从特定的数据库类型中解藕出来,从而非常简单地就可以实现对多种数据库的支持。
  2. 非常方便的调用存储过程、将数据库的结果转成实体类型(或列表)、调用完成后自动“回写”输出参数到实体对象。 只需要一个调用便可实现这三个操作步骤。
  3. 数据访问层可以同时支持多种数据库类型的多个连接。并可以在运行时简单的切换。
  4. 数据访问层可以非常方便地实现类似“多帐套数据库”的支持,即根据不同的客户端请求来切换相应的数据库连接。
  5. 数据访问层同时提供简单或详细的API,连接或事务可以自动控制也可以由上层类来控制。总之就是让您在享受简化的过程中拥有对细节的充分控制机会。
  6. 提供一个辅助(Profiler)工具,让您可以随时了解详细的数据库访问情况:打开了多少次连接,每个连接执行了哪些调用,以及调用的执行时间,调用参数等等。

设计目标:调用存储过程,不管输入参数多么复杂,不管有多少输出参数,包含转换一个结果集到实体列表,只需要一行C#代码。

1. 示范代码,简单地调用单个存储过程

C#实体类型,成员与数据库表对应,这里就不给出表结构截图了。

  1. /// <summary>  
  2. /// 表示一个商品对象的实体类  
  3. /// </summary>  
  4. public sealed class Product  
  5. {  
  6.     public int ProductID { getset; }  
  7.     public string ProductName { getset; }  
  8.     public int CategoryID { getset; }  
  9.     public string Unit { getset; }  
  10.     public decimal UnitPrice { getset; }  
  11.     public int Quantity { getset; }  
  12.  
  13.     // 仅当加载详细信息(单个实体)时才加载它。加载列表时忽略这个字段。  
  14.     [ItemField(OnlyLoadAll = true)]      
  15.     public string Remark { getset; }  

存储过程-更新商品信息

  1. create procedure [dbo].[UpdateProduct](   
  2.     @ProductName nvarchar(50),   
  3.     @CategoryID int,   
  4.     @Unit nvarchar(10),   
  5.     @UnitPrice money,   
  6.     @Quantity int,   
  7.     @Remark nvarchar(max),   
  8.     @ProductID int 
  9. )   
  10. as 
  11. update Products   
  12. set ProductName = @ProductName,   
  13.     CategoryID = @CategoryID,   
  14.     Unit = @Unit,   
  15.     UnitPrice = @UnitPrice,   
  16.     Quantity = @Quantity,   
  17.     Remark = @Remark   
  18. where ProductID = @ProductID; 

C#调用代码

  1. public bool UpdateProduct(Product product)  
  2. {  
  3.     return (FishBLLHelper.CallSpExecuteNonQuery("UpdateProduct", product) > 0);  

存储过程-获取商品列表,支持分页

  1. create procedure [dbo].[GetProductByCategoryId](  
  2.     @CategoryID int,  
  3.     @PageIndex int = 0,  
  4.     @PageSize int = 20,  
  5.     @TotalRecords int output  
  6. )  
  7. as 
  8. begin  
  9.      
  10. declare @ResultTable table  
  11. (  
  12.     RowIndex int,  
  13.     ProductID int,  
  14.     ProductName nvarchar(50),  
  15.     CategoryID int,  
  16.     Unit nvarchar(10),  
  17.     UnitPrice money,  
  18.     Quantity int 
  19. );  
  20.      
  21. insert into @ResultTable  
  22. select row_number() over (order by ProductID asc) as RowIndex,  
  23.        p.ProductID, p.ProductName, p.CategoryID, p.Unit, p.UnitPrice, p.Quantity  
  24. from   Products as p  
  25. where CategoryID = @CategoryID;  
  26.        
  27. select  @TotalRecords = count(*) from  @ResultTable;  
  28.      
  29. select *  
  30. from   @ResultTable  
  31. where  RowIndex > (@PageSize * @PageIndex) and RowIndex <= (@PageSize * (@PageIndex+1));  
  32.      
  33. end; 

C#调用代码

  1. public List<Product> GetProductByCategoryId(int categoryId, ref int pageIndex, int pageSize, out int recCount)  
  2. {  
  3.     return FishBLLHelper.CallSpGetDataItemListPaged<Product>("GetProductByCategoryId",  
  4.    ref pageIndex, pageSize, out recCount, categoryId);  
  5. }  

2. 示范代码,以事务方式调用多个存储过程

C#实体类型,成员与数据库表对应,这里就不给出表结构截图了。

  1. public sealed class OrderItem  
  2. {  
  3.     public int OrderID { getset; }  
  4.     public int? CustomerID { getset; }  
  5.     public DateTime OrderDate { getset; }  
  6.     public decimal SumMoney { getset; }  
  7.     [ItemField(OnlyLoadAll = true)]    // 仅当加载详细信息时才加载它。  
  8.     public string Comment { getset; }  
  9.     public bool Finished { getset; }  
  10.     public string CustomerName { getset; }  
  11.  
  12.     [ItemField(IgnoreLoad=true)]    // 不加载这个成员  
  13.     public List<OrderDetail> Detail;  
  14. }  
  15.  
  16. public sealed class OrderDetail  
  17. {  
  18.     public int OrderID { getset; }  
  19.     public int ProductID { getset; }  
  20.     public decimal UnitPrice { getset; }  
  21.     public int Quantity { getset; }  

三个存储过程,用于插入主表,子表,刷新总金额

  1. create procedure [dbo].[InsertOrder](  
  2.     @CustomerID int = null,  
  3.     @SumMoney money,  
  4.     @Comment nvarchar(300),  
  5.     @OrderID int output  
  6. )  
  7. as 
  8. begin  
  9.    
  10. insert into Orders( CustomerID, OrderDate, SumMoney, Comment)  
  11. values( @CustomerID, getdate(), @SumMoney, @Comment);  
  12.    
  13. set @OrderID = scope_identity();  
  14.    
  15. end;  
  16. create procedure [dbo].[InsertOrderDetail](  
  17.     @OrderID int,  
  18.     @ProductID int,  
  19.     @Quantity int 
  20. )  
  21. as 
  22. declare @Price money;  
  23. select @Price = (select UnitPrice from Products where ProductID = @ProductID);  
  24.  
  25. insert into [Order Details] (OrderID, ProductID, UnitPrice, Quantity)  
  26. values (@OrderID, @ProductID, @Price, @Quantity);  
  27.  
  28.  
  29. create procedure [dbo].[RefreshOrderSumMoney](  
  30.     @OrderID int 
  31. )  
  32. as 
  33. declare @SumMoney money;  
  34. select @SumMoney = (select sum(UnitPrice * Quantity) from [Order Details] where OrderID = @OrderID);  
  35.  
  36. update Orders set SumMoney = @SumMoney  where OrderID = @OrderID;  
  37.  

说明:以上三个存储要求先调用InsertOrder,然后获取新的OrderID后,才能调用后面二个。下面来看看在C#中该如何调用这三个存储过程吧。

C#调用代码

  1. public int AddOrder(OrderItem order)  
  2. {  
  3.     // 以事务的方式创建一个FishDbContext对象,将使用默认的连接字符串  
  4.     using( FishDbContext db = new FishDbContext(true) ) {  
  5.         // 添加记录到表Orders,同时获取新产生ID  
  6.         FishBLLHelper.CallSpExecuteNonQuery(db, "InsertOrder", order);  
  7.           
  8.         // 为订单明细设置OrderId,并添加到表[Order Details]  
  9.         order.Detail.ForEach(x => {  
  10.             x.OrderID = order.OrderID;  
  11.             FishBLLHelper.CallSpExecuteNonQuery(db, "InsertOrderDetail", x);  
  12.         });  
  13.  
  14.         // 刷新订单总金额。  
  15.         FishBLLHelper.CallSpExecuteNonQuery(db, "RefreshOrderSumMoney"null, order.OrderID);  
  16.  
  17.         // 提交事务。  
  18.         db.CommitTransaction();  
  19.  
  20.         return order.OrderID;  
  21.     }  
  22. }  

好了,示例就写到这里,方不方便嘛,自己觉得好用就行。

今天先不谈ORM工具,我们只谈存储过程,下次我会写个关于性能测试的文章来专门谈ORM。

原文链接:http://www.cnblogs.com/fish-li/archive/2011/03/28/1998104.html

【编辑推荐】

  1. DBA应用技巧:如何升级InnoDB Plugin
  2. 一句代码实现批量数据绑定 上
  3. 一句代码实现批量数据绑定 下
  4. MySQL日志操作教程:DBA们管理的利器
  5. MySQL触发器如何正确使用

 

责任编辑:艾婧 来源: 博客园
相关推荐

2011-05-07 12:56:39

数据访问

2012-01-11 09:46:31

DAL

2011-05-10 16:44:43

数据访问层

2011-05-05 14:33:34

数据访问层

2009-08-13 14:59:00

C#数据访问层

2009-04-02 10:37:52

通用基类SQLLINQ

2009-08-19 10:54:42

ASP.NET数据访问

2009-08-04 10:17:55

ASP.NET SqlASP.NET数据访问

2009-09-04 18:00:54

C#数据访问层

2012-06-07 10:53:08

架构设计数据访问层设计原则

2009-07-24 13:25:43

创建数据访问层

2012-08-15 11:03:18

框架项目

2009-07-24 14:15:51

数据访问层

2009-07-24 13:45:28

添加参数化

2012-02-24 09:07:53

云计算成本

2023-07-27 08:16:51

数据访问层项目

2010-10-22 09:43:34

数据库访问层

2009-07-28 09:42:22

.NET数据访问层

2011-11-02 09:01:30

系统架构师

2010-06-08 15:35:55

CAN总线协议
点赞
收藏

51CTO技术栈公众号