C# 如何使用实体框架和MySQL(包括相关实体)获取每个组的最新记录

C# 如何使用实体框架和MySQL(包括相关实体)获取每个组的最新记录,c#,mysql,linq,entity-framework,C#,Mysql,Linq,Entity Framework,我将实体框架与MySQL数据库和DbContext一起使用。我有一个实体“Message”,它有一个相关实体“Sender”。(该“消息”还具有相关实体“接收者”)。我试图编写一个查询,只返回每个接收者的“最新”消息。但在执行此操作时,我还希望加载关联的“发件人”,以便能够访问发件人的一个属性(电子邮件字段),该属性需要包含在返回的数据传输对象中。“MessageDTO”是我返回的数据传输对象,包括消息的id、消息的内容和发件人的电子邮件 如果我将发件人的电子邮件从DTO中排除,则以下查询将准确

我将实体框架与MySQL数据库和DbContext一起使用。我有一个实体“Message”,它有一个相关实体“Sender”。(该“消息”还具有相关实体“接收者”)。我试图编写一个查询,只返回每个接收者的“最新”消息。但在执行此操作时,我还希望加载关联的“发件人”,以便能够访问发件人的一个属性(电子邮件字段),该属性需要包含在返回的数据传输对象中。“MessageDTO”是我返回的数据传输对象,包括消息的id、消息的内容和发件人的电子邮件

如果我将发件人的电子邮件从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,因为发件人为null

如何在查询中加载发件人,以便在DTO中包含发件人的电子邮件

编辑:

根据要求,我将包含由Gert Arnold建议的方法生成的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`}

在分组之前,不需要使用
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.email
    sender
    为null时不会崩溃,因为在SQL中没有null对象引用。整个表达式(
    sender.email
    )只返回null

为什么要使用
SqlQuery
?如果没有它,整个查询将被翻译成SQL,发送者也将加入其中。使用SqlQuery是我能够在“分组依据”之前执行“order by”的唯一方法。如果我用“order by”指令替换SqlQuery,查询将不再返回每个组的最新记录,而是返回每个组的一些记录。我读到“添加ORDER by子句不会影响从每个组中选择值。结果集的排序发生在选择值之后,ORDER by不会影响服务器选择的值”这在“where子句”中给出了一个错误“未知列'Extent1.receiver_id'实际上,我使用的正是你提供的方法。在我的方法中,我所做的额外工作就是将refGroupQuery返回给调用函数。我想知道这是MySQL还是MySQL适配器中的错误。我发现,我确实在使用6.6.5版本的适配器。所以看起来我需要先升级到一个新版本…我是说整个SQL查询:)恐怕很可能会出现错误,但这可能是一个映射问题。
消息
接收者
之间的映射是什么样的?您是否明确命名了
receiver\u id
列,还是依赖EF的默认命名约定?(我假设您首先在这里使用代码)。我已将生成的SQL查询添加到问题中。实际上,我采用了数据库优先的方法,因为我们有一个由RubyonRails提供服务的现有数据库,其中WebAPI功能正在转移到.NET。我不依赖EF的默认命名约定,因为它们在我们的情况下不起作用。“messages”表有一个“receiver_id”字段,它是“users”表中“id”的外键。因此,该消息将有一个接收器。肯定是一个错误
Extent1
的作用域是底部的一个子查询,但在任何地方都可以引用。
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
                           };