今天來曬曬我的通用數據訪問層。
寫了很多年的數據庫項目,數據訪問嘛,一直是用業務實體+存儲過程的方式,因此經常會寫很多調用存儲過程的代碼。這些代碼用Ado.net如何寫,我想大家應該都知道: 創建Connection, 創建Command, 給命令參數一個一個賦值,然后調用,調用完成后,如果有輸出參數,則要讀出來,如果有結果集,則要將結果集轉換成自己的實體列表,這個過程也是非常機械化的。總之,調用任何存儲過程都需要這樣一堆類似的代碼。
我是個喜歡最求完美的人,自然不喜歡每個項目都有這樣一堆機械代碼的存在,于是經過不斷的重構代碼,慢慢的就形成了自己的通用數據訪問層。
我的通用數據訪問層具有以下特點:
1. 可用于訪問各種類型的數據庫,讓您的應用程序從特定的數據庫類型中解藕出來,從而非常簡單地就可以實現對多種數據庫的支持。
2. 非常方便的調用存儲過程、將數據庫的結果轉成實體類型(或列表)、調用完成后自動“回寫”輸出參數到實體對象。 只需要一個調用便可實現這三個操作步驟。
3. 數據訪問層可以同時支持多種數據庫類型的多個連接。并可以在運行時簡單的切換。
4. 數據訪問層可以非常方便地實現類似“多帳套數據庫”的支持,即根據不同的客戶端請求來切換相應的數據庫連接。
5. 數據訪問層同時提供簡單或詳細的API,連接或事務可以自動控制也可以由上層類來控制。總之就是讓您在享受簡化的過程中擁有對細節的充分控制機會。
6. 提供一個輔助(Profiler)工具,
讓您可以隨時了解詳細的數據庫訪問情況:打開了多少次連接,每個連接執行了哪些調用,以及調用的執行時間,調用參數等等。
設計目標:調用存儲過程,不管輸入參數多么復雜,不管有多少輸出參數,包含轉換一個結果集到實體列表,只需要一行C#代碼。
為了能讓您更好了了解這個框架的使用效果,請點擊: 點擊此處進入示例展示及下載頁面
1. 示范代碼,簡單地調用單個存儲過程
C#實體類型,成員與數據庫表對應,這里就不給出表結構截圖了。
/// <summary> /// 表示一個商品對象的實體類 /// </summary> public sealed class Product { public int ProductID { get; set; } public string ProductName { get; set; } public int CategoryID { get; set; } public string Unit { get; set; } public decimal UnitPrice { get; set; } public int Quantity { get; set; } // 僅當加載詳細信息(單個實體)時才加載它。加載列表時忽略這個字段。 [ItemField(OnlyLoadAll = true)] public string Remark { get; set; } }
存儲過程-更新商品信息
create procedure [dbo].[UpdateProduct]( @ProductName nvarchar(50), @CategoryID int, @Unit nvarchar(10), @UnitPrice money, @Quantity int, @Remark nvarchar(max), @ProductID int ) as update Products set ProductName = @ProductName, CategoryID = @CategoryID, Unit = @Unit, UnitPrice = @UnitPrice, Quantity = @Quantity, Remark = @Remark where ProductID = @ProductID;
C#調用代碼
public bool UpdateProduct(Product product) { return (FishBLLHelper.CallSpExecuteNonQuery("UpdateProduct", product) > 0); }
存儲過程-獲取商品列表,支持分頁
create procedure [dbo].[GetProductByCategoryId]( @CategoryID int, @PageIndex int = 0, @PageSize int = 20, @TotalRecords int output ) as begin declare @ResultTable table ( RowIndex int, ProductID int, ProductName nvarchar(50), CategoryID int, Unit nvarchar(10), UnitPrice money, Quantity int ); insert into @ResultTable select row_number() over (order by ProductID asc) as RowIndex, p.ProductID, p.ProductName, p.CategoryID, p.Unit, p.UnitPrice, p.Quantity from Products as p where CategoryID = @CategoryID; select @TotalRecords = count(*) from @ResultTable; select * from @ResultTable where RowIndex > (@PageSize * @PageIndex) and RowIndex <= (@PageSize * (@PageIndex+1)); end;
C#調用代碼
public List<Product> GetProductByCategoryId(int categoryId, ref int pageIndex, int pageSize, out int recCount) { return FishBLLHelper.CallSpGetDataItemListPaged<Product>("GetProductByCategoryId", ref pageIndex, pageSize, out recCount, categoryId); }
2. 示范代碼,以事務方式調用多個存儲過程
C#實體類型,成員與數據庫表對應,這里就不給出表結構截圖了。
public sealed class OrderItem { public int OrderID { get; set; } public int? CustomerID { get; set; } public DateTime OrderDate { get; set; } public decimal SumMoney { get; set; } [ItemField(OnlyLoadAll = true)] // 僅當加載詳細信息時才加載它。 public string Comment { get; set; } public bool Finished { get; set; } public string CustomerName { get; set; } [ItemField(IgnoreLoad=true)] // 不加載這個成員 public List<OrderDetail> Detail; } public sealed class OrderDetail { public int OrderID { get; set; } public int ProductID { get; set; } public decimal UnitPrice { get; set; } public int Quantity { get; set; } }
三個存儲過程,用于插入主表,子表,刷新總金額
create procedure [dbo].[InsertOrder]( @CustomerID int = null, @SumMoney money, @Comment nvarchar(300), @OrderID int output ) as begin insert into Orders( CustomerID, OrderDate, SumMoney, Comment) values( @CustomerID, getdate(), @SumMoney, @Comment); set @OrderID = scope_identity(); end;
create procedure [dbo].[InsertOrderDetail]( @OrderID int, @ProductID int, @Quantity int ) as declare @Price money; select @Price = (select UnitPrice from Products where ProductID = @ProductID); insert into [Order Details] (OrderID, ProductID, UnitPrice, Quantity) values (@OrderID, @ProductID, @Price, @Quantity);
create procedure [dbo].[RefreshOrderSumMoney]( @OrderID int ) as declare @SumMoney money; select @SumMoney = (select sum(UnitPrice * Quantity) from [Order Details] where OrderID = @OrderID); update Orders set SumMoney = @SumMoney where OrderID = @OrderID;
說明:以上三個存儲要求先調用InsertOrder,然后獲取新的OrderID后,才能調用后面二個。下面來看看在C#中該如何調用這三個存儲過程吧。
C#調用代碼
public int AddOrder(OrderItem order) { // 以事務的方式創建一個FishDbContext對象,將使用默認的連接字符串 using( FishDbContext db = new FishDbContext(true) ) { // 添加記錄到表Orders,同時獲取新產生ID FishBLLHelper.CallSpExecuteNonQuery(db, "InsertOrder", order); // 為訂單明細設置OrderId,并添加到表[Order Details] order.Detail.ForEach(x => { x.OrderID = order.OrderID; FishBLLHelper.CallSpExecuteNonQuery(db, "InsertOrderDetail", x); }); // 刷新訂單總金額。 FishBLLHelper.CallSpExecuteNonQuery(db, "RefreshOrderSumMoney", null, order.OrderID); // 提交事務。 db.CommitTransaction(); return order.OrderID; } }
好了,示例就寫到這里,方不方便嘛,自己覺得好用就行。
更多說明請參考下載壓縮包中的用戶手冊。點擊此處進入示例展示及下載頁面
Fish Li (李奇峰)
浙公網安備 33010602011771號