一、什么是DTO
先來看看百度百科的解釋:
數據傳輸對象(DTO)(Data Transfer Object),是一種設計模式之間傳輸數據的軟件應用系統。數據傳輸目標往往是數據訪問對象從數據庫中檢索數據。數據傳輸對象與數據交互對象或數據訪問對象之間的差異是一個以不具有任何行為除了存儲和檢索的數據(訪問和存取器)。
二、為什么需要DTO
在一個軟件系統的實現中,我們常常需要訪問數據庫,並將從數據庫中所取得的數據顯示在用戶界面上。這樣做的一個問題是:用於在用戶界面上展示的數據模型和從數據庫中取得的數據模型常常具有較大區別。在這種情況下,我們常常需要向服務端發送多個請求才能將用於在頁面中展示的數據湊齊。
三、使用Dapper實現DTO
使用Dapper可以直接返回DTO類型,包括兩種方式:
新建Category、ProductDetail和ProductDTO實體類:
Category實體類定義如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DapperConvertDto 8 { 9 public class Category 10 { 11 public int CategoryId { get; set; } 12 public string CategoryName { get; set; } 13 } 14 }
ProductDetail實體類定義如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DapperConvertDto 8 { 9 public class ProductDetail 10 { 11 public int ProductId { get; set; } 12 13 public string ProductName { get; set; } 14 15 public double Price { get; set; } 16 17 public int CategoryId { get; set; } 18 } 19 }
ProductDTO實體類定義如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DapperConvertDto 8 { 9 public class ProductDto 10 { 11 public int ProductId { get; set; } 12 13 public string ProductName { get; set; } 14 15 public double ProductPrice { get; set; } 16 17 public string CategoryName { get; set; } 18 } 19 }
ProductDTO實體類中的ProductPrice對應ProductDetail表的Price,CategoryName對應Category表的CategoryName。
方式一:直接在SQL語句中使用as
1 using System; 2 using System.Collections.Generic; 3 using System.Data.SqlClient; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 using Dapper; 8 9 namespace DapperConvertDto 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 // 數據庫連接 16 string strCon = @"Initial Catalog=StudentSystem; Integrated Security=False;User Id=sa;Password=1qaz@WSX;Data Source=127.0.0.1;Failover Partner=127.0.0.1;Application Name=TransForCCT"; 17 SqlConnection conn = new SqlConnection(strCon); 18 19 // 方式一:直接在SQL語句中使用as,將查詢的字段轉換成DTO類型的屬性 20 string strSql = @" SELECT p.ProductId,p.ProductName,p.Price AS ProductPrice,c.CategoryName FROM Category c INNER JOIN ProductDetail p ON c.CategoryId=p.CategoryId "; 21 ProductDto product = conn.Query<ProductDto>(strSql).FirstOrDefault<ProductDto>(); 22 } 23 } 24 }
結果:
從截圖中看出,返回的就是想要的DTO類型。
方式二:使用委托的方式進行映射,分別把Category和ProductDetail實體類里的屬性,映射成ProductDTO類型的屬性:
1 using System; 2 using System.Collections.Generic; 3 using System.Data.SqlClient; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 using Dapper; 8 9 namespace DapperConvertDto 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 // 數據庫連接 16 string strCon = @"Initial Catalog=StudentSystem; Integrated Security=False;User Id=sa;Password=1qaz@WSX;Data Source=127.0.0.1;Failover Partner=127.0.0.1;Application Name=TransForCCT"; 17 SqlConnection conn = new SqlConnection(strCon); 18 19 // 方式一:直接在SQL語句中使用as,將查詢的字段轉換成DTO類型的屬性 20 string strSql = @" SELECT p.ProductId,p.ProductName,p.Price AS ProductPrice,c.CategoryName FROM Category c INNER JOIN ProductDetail p ON c.CategoryId=p.CategoryId "; 21 ProductDto product = conn.Query<ProductDto>(strSql).FirstOrDefault<ProductDto>(); 22 23 // 方式二:使用委托進行自定義映射 24 string strSql2 = @" SELECT p.ProductId,p.ProductName,p.Price,c.CategoryName FROM Category c INNER JOIN ProductDetail p ON c.CategoryId=p.CategoryId "; 25 // 定義映射的委托 26 Func<ProductDetail, Category, ProductDto> map = (p, c) => 27 { 28 ProductDto dto = new ProductDto(); 29 dto.ProductId = p.ProductId; 30 dto.ProductName = p.ProductName; 31 dto.ProductPrice = p.Price; 32 dto.CategoryName = c.CategoryName; 33 return dto; 34 }; 35 // splitOn表示查詢的SQL語句中根據哪個字段進行分割 36 string splitOn = "CategoryName"; 37 List<ProductDto> list = conn.Query<ProductDetail, Category, ProductDto>(strSql2, map, splitOn: splitOn).ToList<ProductDto>(); 38 } 39 } 40 }
結果:
注意:
1、splitOn
splitOn表示查詢的SQL語句中按照哪個字段進行分割,splitOn的順序是從右向左的,遇到splitOn設置的字段接結束,把從右邊開始到設置的這個字段歸為同一個實體。例如:上面的例子中,splitOn設置為CategoryName,則表示從右邊開始,到CategoryName為止的所有字段都是屬於Category這個實體的,剩余的字段都是屬於ProductDetail實體的。
2、注意委托中實體類的前后順序
委托中實體類的前后順序一定要和查詢的SQL語句中字段的前后順序一致,上面的例子中先查詢的ProductDetail、后查詢的Category,那么定義委托的時候,要先寫ProductDetail,后寫Category,如果委托中實體類的順序錯了,那么不會得到映射的數據,看下面的例子:
1 using System; 2 using System.Collections.Generic; 3 using System.Data.SqlClient; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 using Dapper; 8 9 namespace DapperConvertDto 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 // 數據庫連接 16 string strCon = @"Initial Catalog=StudentSystem; Integrated Security=False;User Id=sa;Password=1qaz@WSX;Data Source=127.0.0.1;Failover Partner=127.0.0.1;Application Name=TransForCCT"; 17 SqlConnection conn = new SqlConnection(strCon); 18 19 // 方式一:直接在SQL語句中使用as,將查詢的字段轉換成DTO類型的屬性 20 string strSql = @" SELECT p.ProductId,p.ProductName,p.Price AS ProductPrice,c.CategoryName FROM Category c INNER JOIN ProductDetail p ON c.CategoryId=p.CategoryId "; 21 ProductDto product = conn.Query<ProductDto>(strSql).FirstOrDefault<ProductDto>(); 22 23 // 方式二:使用委托進行自定義映射 24 string strSql2 = @" SELECT p.ProductId,p.ProductName,p.Price,c.CategoryName FROM Category c INNER JOIN ProductDetail p ON c.CategoryId=p.CategoryId "; 25 // 定義映射的委托 26 //Func<ProductDetail, Category, ProductDto> map = (p, c) => 27 //{ 28 // ProductDto dto = new ProductDto(); 29 // dto.ProductId = p.ProductId; 30 // dto.ProductName = p.ProductName; 31 // dto.ProductPrice = p.Price; 32 // dto.CategoryName = c.CategoryName; 33 // return dto; 34 //}; 35 36 // 錯誤的委托 37 Func<Category, ProductDetail, ProductDto> map = (c,p) => 38 { 39 ProductDto dto = new ProductDto(); 40 dto.ProductId = p.ProductId; 41 dto.ProductName = p.ProductName; 42 dto.ProductPrice = p.Price; 43 dto.CategoryName = c.CategoryName; 44 return dto; 45 }; 46 47 // splitOn表示查詢的SQL語句中根據哪個字段進行分割 48 string splitOn = "CategoryName"; 49 List<ProductDto> list = conn.Query< Category, ProductDetail, ProductDto>(strSql2, map, splitOn: splitOn).ToList<ProductDto>(); 50 } 51 } 52 }
結果: