首页 运维 正文
晒晒我的通用数据访问层

 2022-10-23    338  

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

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

晒晒我的通用数据访问层

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

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

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

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

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

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

    ///<summary> 
    ///表示一个商品对象的实体类 
    ///</summary> 
    publicsealedclassProduct 
    { 
    publicintProductID{get;set;} 
    publicstringProductName{get;set;} 
    publicintCategoryID{get;set;} 
    publicstringUnit{get;set;} 
    publicdecimalUnitPrice{get;set;} 
    publicintQuantity{get;set;} 
    
    //仅当加载详细信息(单个实体)时才加载它。加载列表时忽略这个字段。 
    [ItemField(OnlyLoadAll=true)] 
    publicstringRemark{get;set;} 
    }
    

    存储过程-更新商品信息

    createprocedure[dbo].[UpdateProduct]( 
    @ProductNamenvarchar(50), 
    @CategoryIDint, 
    @Unitnvarchar(10), 
    @UnitPricemoney, 
    @Quantityint, 
    @Remarknvarchar(max), 
    @ProductIDint
    ) 
    as
    updateProducts 
    setProductName=@ProductName, 
    CategoryID=@CategoryID, 
    Unit=@Unit, 
    UnitPrice=@UnitPrice, 
    Quantity=@Quantity, 
    Remark=@Remark 
    whereProductID=@ProductID;
    

    C#调用代码

    publicboolUpdateProduct(Productproduct) 
    { 
    return(FishBLLHelper.CallSpExecuteNonQuery("UpdateProduct",product)>0); 
    }
    

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

    createprocedure[dbo].[GetProductByCategoryId]( 
    @CategoryIDint, 
    @PageIndexint=0, 
    @PageSizeint=20, 
    @TotalRecordsintoutput 
    ) 
    as
    begin 
     
    declare@ResultTabletable 
    ( 
    RowIndexint, 
    ProductIDint, 
    ProductNamenvarchar(50), 
    CategoryIDint, 
    Unitnvarchar(10), 
    UnitPricemoney, 
    Quantityint
    ); 
     
    insertinto@ResultTable 
    selectrow_number()over(orderbyProductIDasc)asRowIndex, 
    p.ProductID,p.ProductName,p.CategoryID,p.Unit,p.UnitPrice,p.Quantity 
    fromProductsasp 
    whereCategoryID=@CategoryID; 
     
    select@TotalRecords=count(*)from@ResultTable; 
     
    select* 
    from@ResultTable 
    whereRowIndex>(@PageSize*@PageIndex)andRowIndex<=(@PageSize*(@PageIndex+1)); 
     
    end;
    

    C#调用代码

    publicList<Product>GetProductByCategoryId(intcategoryId,refintpageIndex,intpageSize,outintrecCount) 
    { 
    returnFishBLLHelper.CallSpGetDataItemListPaged<Product>("GetProductByCategoryId", 
    refpageIndex,pageSize,outrecCount,categoryId); 
    } 
    

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

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

    publicsealedclassOrderItem 
    { 
    publicintOrderID{get;set;} 
    publicint?CustomerID{get;set;} 
    publicDateTimeOrderDate{get;set;} 
    publicdecimalSumMoney{get;set;} 
    [ItemField(OnlyLoadAll=true)]//仅当加载详细信息时才加载它。 
    publicstringComment{get;set;} 
    publicboolFinished{get;set;} 
    publicstringCustomerName{get;set;} 
    
    [ItemField(IgnoreLoad=true)]//不加载这个成员 
    publicList<OrderDetail>Detail; 
    } 
    
    publicsealedclassOrderDetail 
    { 
    publicintOrderID{get;set;} 
    publicintProductID{get;set;} 
    publicdecimalUnitPrice{get;set;} 
    publicintQuantity{get;set;} 
    }
    

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

    createprocedure[dbo].[InsertOrder]( 
    @CustomerIDint=null, 
    @SumMoneymoney, 
    @Commentnvarchar(300), 
    @OrderIDintoutput 
    ) 
    as
    begin 
     
    insertintoOrders(CustomerID,OrderDate,SumMoney,Comment) 
    values(@CustomerID,getdate(),@SumMoney,@Comment); 
     
    set@OrderID=scope_identity(); 
     
    end; 
    createprocedure[dbo].[InsertOrderDetail]( 
    @OrderIDint, 
    @ProductIDint, 
    @Quantityint
    ) 
    as
    declare@Pricemoney; 
    select@Price=(selectUnitPricefromProductswhereProductID=@ProductID); 
    
    insertinto[OrderDetails](OrderID,ProductID,UnitPrice,Quantity) 
    values(@OrderID,@ProductID,@Price,@Quantity); 
    
    
    createprocedure[dbo].[RefreshOrderSumMoney]( 
    @OrderIDint
    ) 
    as
    declare@SumMoneymoney; 
    select@SumMoney=(selectsum(UnitPrice*Quantity)from[OrderDetails]whereOrderID=@OrderID); 
    
    updateOrderssetSumMoney=@SumMoneywhereOrderID=@OrderID; 
    
    

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

    C#调用代码

    publicintAddOrder(OrderItemorder) 
    { 
    //以事务的方式创建一个FishDbContext对象,将使用默认的连接字符串 
    using(FishDbContextdb=newFishDbContext(true)){ 
    //添加记录到表Orders,同时获取新产生ID 
    FishBLLHelper.CallSpExecuteNonQuery(db,"InsertOrder",order); 
     
    //为订单明细设置OrderId,并添加到表[OrderDetails] 
    order.Detail.ForEach(x=>{ 
    x.OrderID=order.OrderID; 
    FishBLLHelper.CallSpExecuteNonQuery(db,"InsertOrderDetail",x); 
    }); 
    
    //刷新订单总金额。 
    FishBLLHelper.CallSpExecuteNonQuery(db,"RefreshOrderSumMoney",null,order.OrderID); 
    
    //提交事务。 
    db.CommitTransaction(); 
    
    returnorder.OrderID; 
    } 
    } 
    

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

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

原文链接:https://77isp.com/post/7646.html

=========================================

https://77isp.com/ 为 “云服务器技术网” 唯一官方服务平台,请勿相信其他任何渠道。