2011-09-19 28 views
75

我正在嘗試使用Dapper的Multimapping功能來返回ProductItem和相關客戶的列表。在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; } 
} 

我短小精悍的代碼如下

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" 
); 

這工作得很好,但我似乎有整列列表添加到splitOn參數返回所有的客戶屬性。如果我不添加「CustomerName」,它將返回null。我是否理解多圖功能的核心功能?我不想每次都添加一個完整的列名稱列表。

+0

你怎麼實際在datagridview中顯示這兩個表呢?一個小例子將不勝感激。 –

回答

124

我只是跑一個測試,工作正常:

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); 

的splitOn PARAM需要被指定爲分割點,它默認爲標識。如果有多個分割點,則需要將它們添加到以逗號分隔的列表中。

說你的記錄是這樣的:

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

小巧玲瓏需要知道如何在列順序分成2個對象。粗略的外觀顯示客戶起始於CustomerId列,因此splitOn: CustomerId

有一個警告這裏,如果在基礎表列的順序被翻轉出於某種原因:

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

splitOn: CustomerId將導致一個空的客戶名稱。

如果您指定CustomerId,CustomerName爲分割點,則dapper會假設您試圖將結果集分成3個對象。首先從第一個開始,第二個開始於CustomerId,第三個開始於CustomerName

+0

謝謝薩姆。是的,你的權利是與CustomerName有關的列的返回順序CustomerId被返回CustomerName正在返回null。 –

+6

有一點要記住,你不能在'spliton'中有空格,即'CustomerId,CustomerName'不是'CustomerId,CustomerName',因爲Dapper不會'修剪'字符串拆分的結果。它只會拋出通用的spliton錯誤。有一天讓我瘋狂。 – jes

+0

如果我想獲得針對客戶的產品清單,該怎麼辦?在Dapper.Net中如何處理這種情況? – touseefkhan4pk

2

還有一個警告。如果CustomerId字段爲空(通常在具有左連接的查詢中),則Dapper使用Customer = null創建ProductItem。在上面的例子中:

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); 

甚至還有一個警告/陷阱。如果您未映射在splitOn中指定的字段,並且該字段包含null,則Dapper將創建並填充相關對象(在此情況下爲Customer)。爲了演示如何使用這個類與以前的SQL:

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

除了將Customerid添加到類中之外,還有第二個示例的解決方案嗎?我有一個問題,我需要一個空對象,但它給了我一個空對象。 (http://stackoverflow.com/questions/27231637/dapper-left-joins-not-returning-null-object-but-an-empty-object) –

0

我一般在我的回購,這對我的用例很好。我以爲我會分享。也許有人會進一步擴大這一點。

有些缺點是:

  • 這裏假設你的外鍵的屬性是你的孩子對象的名稱+「ID」,例如單元ID。
  • 我只有它映射1個子對象父。

代碼:

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

我們的表命名爲類似於你的,哪裏像「客戶ID」可以使用「SELECT *」操作返回兩次。因此,小巧玲瓏的是做自己的工作,但只是分裂太早(可能),因爲該列是:

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

這使得spliton:參數不那麼有用,尤其是當你不知道什麼順序列返回。當然,你可以手動指定列...但它是2017年,我們很少再爲基本對象獲取。

我們做了什麼,並且它在很多年裏對數千次查詢都很有用,只是使用Id的別名,並且從不指定spliton(使用Dapper的默認「Id」)。

select 
p.*, 

c.CustomerID AS Id, 
c.* 

...瞧! Dapper默認只會在ID上分割,並且該ID在所有Customer列之前出現。當然,它會在你的返回結果集中添加一個額外的列,但這對於確切知道哪些列屬於哪個對象的附加實用程序來說,是非常小的開銷。你可以很容易地擴展它。需要地址和國家信息?

select 
p.*, 

c.CustomerID AS Id, 
c.*, 

address.AddressID AS Id, 
address.*, 

country.CountryID AS Id, 
country.* 

最重要的是,您清楚地顯示了sql的最小數量,哪些列與哪個對象相關聯。其餘的就是Dapper。

+1

這很美麗。謝謝! –