2013-10-03 55 views
0

我正在使用實體框架與MySQL數據庫和DbContext。我有一個實體「消息」,有一個相關的實體「發件人」。 (「消息」也有一個相關的實體「接收者」)。我正在嘗試編寫一個查詢,它將只返回每個接收者的「最新」消息。但是當我這樣做時,我也想加載關聯的「發件人」,以便我可以訪問我要返回的數據傳輸對象中包含的發件人屬性(電子郵件字段)之一。 「MessageDTO」是我返回的數據傳輸對象,包括消息的標識,消息的內容和發件人的電子郵件。如何獲取每個組使用實體框架和MySQL包括相關實體的最新記錄

如果我排除發件人的電子郵件從DTO,那麼下面的查詢返回正是我需要的(即針對每個接收最新消息):

var refGroupQuery = (from m in dbContext.Messages.SqlQuery("select * from messages order by created_at desc") 
    group m by m.receiver_id into refGroup 
    select new MessageDTO { id = refGroup.FirstOrDefault().id, content = refGroup.FirstOrDefault().content}); 

然而,上述說法不加載與該消息相關聯的發送者,所以當我再包括髮送者的電子郵件中DTO我得到一個NullReferenceException,如下圖所示:

var refGroupQuery = (from m in dbContext.Messages.SqlQuery("select * from messages order by created_at desc") 
    group m by m.receiver_id into refGroup 
    select new MessageDTO { id = refGroup.FirstOrDefault().id, content = refGroup.FirstOrDefault().content, sender_email = refGroup.FirstOrDefault().sender.email}); 

refGroup.FirstOrDefault()sender.email拋出一個NullReferenceException因爲發件人爲空。 。

如何在查詢中加載發件人,以便我可以將發件人的電子郵件包含在我的DTO中?

編輯:

按照要求,我包括獲取由格特·阿諾德的建議方法生成的SQL:

{SELECT 
1 AS `C1`, 
`Apply1`.`id`, 
`Apply1`.`sender_id`, 
`Apply1`.`RECEIVER_ID1` AS `receiver_id`, 
`Apply1`.`created_at`, 
`Apply1`.`read_status`, 
`Extent3`.`email` 
FROM (SELECT 
`Distinct1`.`receiver_id`, 
(SELECT 
`Project2`.`id` 
FROM (SELECT 
`Extent2`.`id`, 
`Extent2`.`sender_id`, 
`Extent2`.`receiver_id`, 
`Extent2`.`created_at`, 
`Extent2`.`read_status` 
FROM `messages` AS `Extent2` 
WHERE (`Extent1`.`receiver_id` = `Extent2`.`receiver_id`) OR ((`Extent1`.`receiver_id` IS NULL) AND (`Extent2`.`receiver_id` IS NULL))) AS `Project2` LIMIT 1) AS `id`, 
(SELECT 
`Project2`.`sender_id` 
FROM (SELECT 
`Extent2`.`id`, 
`Extent2`.`sender_id`, 
`Extent2`.`receiver_id`, 
`Extent2`.`content`, 
`Extent2`.`created_at`, 
`Extent2`.`read_status` 
FROM `messages` AS `Extent2` 
WHERE (`Extent1`.`receiver_id` = `Extent2`.`receiver_id`) OR ((`Extent1`.`receiver_id` IS NULL) AND (`Extent2`.`receiver_id` IS NULL))) AS `Project2` LIMIT 1) AS `sender_id`, 
(SELECT 
`Project2`.`receiver_id` 
FROM (SELECT 
`Extent2`.`id`, 
`Extent2`.`sender_id`, 
`Extent2`.`receiver_id`, 
`Extent2`.`content`, 
`Extent2`.`created_at`, 
`Extent2`.`read_status` 
FROM `messages` AS `Extent2` 
WHERE (`Extent1`.`receiver_id` = `Extent2`.`receiver_id`) OR ((`Extent1`.`receiver_id` IS NULL) AND (`Extent2`.`receiver_id` IS NULL))) AS `Project2` LIMIT 1) AS `RECEIVER_ID1`, 
(SELECT 
`Project2`.`receivable_type` 
FROM (SELECT 
`Extent2`.`id`, 
`Extent2`.`sender_id`, 
`Extent2`.`receiver_id`, 
`Extent2`.`content`, 
`Extent2`.`created_at`, 
`Extent2`.`read_status` 
WHERE (`Extent1`.`receiver_id` = `Extent2`.`receiver_id`) OR ((`Extent1`.`receiver_id` IS NULL) AND (`Extent2`.`receiver_id` IS NULL))) AS `Project2` LIMIT 1) AS `content`, 
(SELECT 
`Project2`.`created_at` 
FROM (SELECT 
`Extent2`.`id`, 
`Extent2`.`sender_id`, 
`Extent2`.`receiver_id`, 
`Extent2`.`content`, 
`Extent2`.`created_at`, 
`Extent2`.`read_status` 
FROM `messages` AS `Extent2` 
WHERE (`Extent1`.`receiver_id` = `Extent2`.`receiver_id`) OR ((`Extent1`.`receiver_id` IS NULL) AND (`Extent2`.`receiver_id` IS NULL))) AS `Project2` LIMIT 1) AS `created_at`, 
(SELECT 
`Project2`.`updated_at` 
FROM (SELECT 
`Extent2`.`id`, 
`Extent2`.`sender_id`, 
`Extent2`.`receiver_id`, 
`Extent2`.`content`, 
`Extent2`.`created_at`, 
`Extent2`.`read_status` 
FROM `messages` AS `Extent2` 
WHERE (`Extent1`.`receiver_id` = `Extent2`.`receiver_id`) OR ((`Extent1`.`receiver_id` IS NULL) AND (`Extent2`.`receiver_id` IS NULL))) AS `Project2` LIMIT 1) AS `read_status` 
FROM (SELECT DISTINCT 
`Extent1`.`receiver_id` 
FROM `messages` AS `Extent1`) AS `Distinct1`) AS `Apply1` LEFT OUTER JOIN `users` AS `Extent3` ON `Apply1`.`sender_id` = `Extent3`.`id`} 
+0

爲什麼使用'SqlQuery'?沒有它,整個查詢將被翻譯成SQL,並且發件人將被加入。 –

+0

使用SqlQuery是我能夠在「group by」之前執行「order by」的唯一方法。如果我將SqlQuery替換爲「order by」指令,則查詢不會再返回每個組的最新記錄,而是僅返回每個組的一些記錄。我在http://stackoverflow.com/questions/5140785/mysql-order-before-group-中讀到,「每個組中的值的選擇不會受到添加ORDER BY子句的影響。結果集的排序發生在值已被選擇,並且ORDER BY不會影響服務器選擇哪個值「 – Stanley

回答

0

你不需要SqlQuery構建分組之前做排序:

var refGroupQuery = from m in dbContext.Messages 
    group m by m.receiver_id into refGroup 
    let firstItem = refGroup.OrderByDescending(x => x.created_at) 
          .FirstOrDefault() 
    select new MessageDTO { 
           id = firstItem.id, 
           content = firstItem.content, 
           sender_email = firstItem.sender.email 
          }; 

這也是一樣的,但它將整個語句翻譯成SQL,它有兩個優點

  • sender不延遲加載每個消息
  • sender.emailsender爲空不會崩潰,因爲在SQL沒有空對象引用。整個表達式(sender.email)只是返回null。
+0

這給出了一個錯誤」Where子句'中的未知列'Extent1.receiver_id'「 – Stanley

+0

我實際上完全使用您提供的方法。我在我的方法中另外做的是將refGroupQuery返回給調用函數。我想知道這是否是MySQL或MySQL適配器中的錯誤。我發現http://bugs.mysql.com/bug.php?id=68513,並且確實使用了適配器的版本6.6.5。所以看起來我需要先升級到新版本... – Stanley

+0

我的意思是整個SQL查詢:)我害怕一個錯誤很可能,但它可能是一個映射問題。 'Message'和'Receiver'之間的映射是什麼樣的?您是否明確指定了'receiver_id'列,或者您是否依賴EF的默認命名約定? (我假設你先在這裏工作代碼)。 –