2011-09-19 21 views
75

Tôi đang cố gắng sử dụng tính năng Multimapping của công cụ lập bản đồ để trả về danh sách ProductItems và Khách hàng được liên kết.Sử dụng đúng cách ghép kênh trong Dapper

[Table("Product")] 
public class ProductItem 
{ 
    public decimal ProductID { get; set; }   
    public string ProductName { get; set; } 
    public string AccountOpened { get; set; } 
    public Customer Customer { get; set; } 
} 

public class Customer 
{ 
    public decimal CustomerId { get; set; } 
    public string CustomerName { get; set; } 
} 

đang đoan trang của tôi là như sau

var sql = @"select * from Product p 
      inner join Customer c on p.CustomerId = c.CustomerId 
      order by p.ProductName"; 

var data = con.Query<ProductItem, Customer, ProductItem>(
    sql, 
    (productItem, customer) => { 
     productItem.Customer = customer; 
     return productItem; 
    }, 
    splitOn: "CustomerId,CustomerName" 
); 

này hoạt động tốt nhưng tôi dường như có thêm danh sách cột đầy đủ để tham số splitOn trả lại tất cả các thuộc tính khách hàng. Nếu tôi không thêm "CustomerName", nó trả về null. Tôi có hiểu nhầm chức năng cốt lõi của tính năng ghép kênh hay không. Tôi không muốn phải thêm một danh sách đầy đủ các tên cột mỗi lần.

+0

làm cách nào để bạn thực sự hiển thị cả hai bảng trong datagridview? một ví dụ nhỏ sẽ được đánh giá cao. –

Trả lời

124

Tôi chỉ cần chạy một bài kiểm tra đó hoạt động tốt:

var sql = "select cast(1 as decimal) ProductId, 'a' ProductName, 'x' AccountOpened, cast(1 as decimal) CustomerId, 'name' CustomerName"; 

var item = connection.Query<ProductItem, Customer, ProductItem>(sql, 
    (p, c) => { p.Customer = c; return p; }, splitOn: "CustomerId").First(); 

item.Customer.CustomerId.IsEqualTo(1); 

Các splitOn param cần phải được quy định như điểm phân chia, nó mặc định Id. Nếu có nhiều điểm phân tách, bạn sẽ cần thêm chúng vào danh sách được phân cách bằng dấu phẩy.

Say recordset của bạn trông như thế này:

 
ProductID | ProductName | AccountOpened | CustomerId | CustomerName 
--------------------------------------- ------------------------- 

Dapper cần phải biết làm thế nào để phân chia các cột theo thứ tự này thành 2 đối tượng. Một cái nhìn lướt qua cho thấy rằng Khách hàng bắt đầu tại cột CustomerId, do đó splitOn: CustomerId.

Có một lớn báo trước đây, nếu thứ tự cột trong bảng cơ bản là lộn vì một lý do:

 
ProductID | ProductName | AccountOpened | CustomerName | CustomerId 
--------------------------------------- ------------------------- 

splitOn: CustomerId sẽ dẫn đến một tên khách hàng null.

Nếu bạn chỉ định CustomerId,CustomerName làm điểm chia nhỏ, công cụ xác định giả định bạn đang cố gắng chia bộ kết quả thành 3 đối tượng. Đầu tiên bắt đầu từ đầu, thứ hai bắt đầu tại CustomerId, thứ ba tại CustomerName.

+0

Cảm ơn Sam. Vâng quyền của bạn đó là thứ tự trả về của các cột là vấn đề với CustomerName | CustomerId đang được trả lại CustomerName đã quay trở lại null. –

+6

Một điều cần nhớ là bạn không thể có khoảng trống trong 'spliton', nghĩa là' CustomerId, CustomerName' không phải 'CustomerId, CustomerName', vì Dapper không' Trim' kết quả của việc tách chuỗi. Nó sẽ chỉ ném lỗi spliton chung. Làm tôi điên một ngày. – jes

+0

nếu tôi muốn nhận danh sách sản phẩm đối với khách hàng thì sao? Làm thế nào sẽ xử lý kịch bản này trong Dapper.Net? – touseefkhan4pk

2

Có một cảnh báo nữa. Nếu trường CustomerId là null (thường là trong các truy vấn với phép nối trái), Dapper tạo ProductItem với Customer = null. Trong ví dụ trên:

var sql = "select cast(1 as decimal) ProductId, 'a' ProductName, 'x' AccountOpened, cast(null as decimal) CustomerId, 'n' CustomerName"; 
var item = connection.Query<ProductItem, Customer, ProductItem>(sql, (p, c) => { p.Customer = c; return p; }, splitOn: "CustomerId").First(); 
Debug.Assert(item.Customer == null); 

Và thậm chí một cảnh báo/bẫy nữa. Nếu bạn không ánh xạ trường được chỉ định trong splitOn và trường đó chứa null Dapper tạo và điền vào đối tượng liên quan (Khách hàng trong trường hợp này). Để chứng minh sử dụng lớp này với sql trước đó:

public class Customer 
{ 
    //public decimal CustomerId { get; set; } 
    public string CustomerName { get; set; } 
} 
... 
Debug.Assert(item.Customer != null); 
Debug.Assert(item.Customer.CustomerName == "n"); 
+0

có giải pháp cho ví dụ thứ hai ngoài việc thêm Customerid vào lớp không? Tôi đang gặp một vấn đề mà tôi cần một đối tượng null, nhưng nó mang lại cho tôi một đối tượng trống. (http://stackoverflow.com/questions/27231637/dapper-left-joins-not-returning-null-object-but-an-empty-object) –

0

Tôi làm điều này một cách tổng quát trong repo, hoạt động tốt cho trường hợp sử dụng của tôi. Tôi nghĩ tôi sẽ chia sẻ. Có thể ai đó sẽ mở rộng thêm điều này.

Một số nhược điểm là:

  • này giả định tính chính nước ngoài của bạn là tên của đối tượng trẻ + "Id", ví dụ UnitId.
  • Tôi có nó chỉ ánh xạ 1 đối tượng con cho phụ huynh.

Mã:

public IEnumerable<TParent> GetParentChild<TParent, TChild>() 
    { 
     var sql = string.Format(@"select * from {0} p 
     inner join {1} c on p.{1}Id = c.Id", 
     typeof(TParent).Name, typeof(TChild).Name); 

     Debug.WriteLine(sql); 

     var data = _con.Query<TParent, TChild, TParent>(
      sql, 
      (p, c) => 
      { 
       p.GetType().GetProperty(typeof (TChild).Name).SetValue(p, c); 
       return p; 
      }, 
      splitOn: typeof(TChild).Name + "Id"); 

     return data; 
    } 
6

bảng của chúng tôi được đặt tên tương tự như của bạn, nơi mà một cái gì đó như "CustomerID" có thể được trả lại hai lần sử dụng một 'chọn *' hoạt động. Do đó, Dapper đang làm công việc của mình nhưng chỉ tách quá sớm (có thể), bởi vì các cột sẽ là:

(select * might return): 
ProductID, 
ProductName, 
CustomerID, --first CustomerID 
AccountOpened, 
CustomerID, --second CustomerID, 
CustomerName. 

Điều này làm cho spliton: tham số không quá hữu ích, đặc biệt là khi bạn không chắc chắn những gì đặt các cột được trả về. Tất nhiên bạn có thể chỉ định các cột theo cách thủ công ... nhưng năm 2017 và chúng tôi hiếm khi làm điều đó nữa cho đối tượng cơ bản.

Những gì chúng tôi làm và nó hoạt động tốt cho hàng nghìn truy vấn trong nhiều năm, chỉ đơn giản là sử dụng bí danh cho Id và không bao giờ chỉ định phân chia (sử dụng 'Id' mặc định của Dapper).

select 
p.*, 

c.CustomerID AS Id, 
c.* 

... voila! Người lập bản đồ sẽ chỉ chia nhỏ trên Id theo mặc định và Id đó xảy ra trước tất cả các cột Khách hàng. Tất nhiên nó sẽ thêm một cột bổ sung vào kết quả trả về của bạn, nhưng đó là chi phí cực kỳ tối thiểu cho tiện ích bổ sung khi biết chính xác các cột nào thuộc về đối tượng nào. Và bạn có thể dễ dàng mở rộng điều này. Cần địa chỉ và thông tin quốc gia?

select 
p.*, 

c.CustomerID AS Id, 
c.*, 

address.AddressID AS Id, 
address.*, 

country.CountryID AS Id, 
country.* 

Hơn hết, bạn rõ ràng hiển thị trong số lượng tối thiểu sql mà cột được liên kết với đối tượng đó. Dapper làm phần còn lại.

+1

Điều này thật đẹp. Cảm ơn! –

Các vấn đề liên quan