I am using Entity Framework with a MySQL Database and DbContext. I have an entity "Message" that has a related entity "Sender". (The "Message" also has a related entity "Receiver"). I am trying to write a query that will return only the "latest" message for each receiver. But when I do this, I also want to load the associated "Sender" so that I can get access to one of the Sender's properties (e-mail field) which I need to include in my Data Transfer Object I am returning. "MessageDTO" is the Data Transfer Object I am returning that includes the id of the message, the content of the message and the sender's email.
If I exclude the sender's email from the DTO, then the following query returns exactly what I need (namely, the latest message for each receiver):
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});
However, the above statement does not load the Sender that is associated with the Message so when I re-include the sender's email in the DTO I get a NullReferenceException as shown below:
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 throws a NullReferenceException because sender is null.
How can I load the Sender in the query so that I can include the Sender's email in my DTO?
EDIT:
As requested, I include the SQL that gets generated by Gert Arnold's suggested method:
{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`}
You don't need the SqlQuery
construct to do the ordering before the grouping:
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
};
This does the same, but it translates the whole statement into SQL, which has two advantages
sender
is not lazy loaded for each message sender.email
does not crash when sender
is null, because in SQL there is no null object reference. The whole expression ( sender.email
) just returns null.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.