简体   繁体   English

简化 Entity Framework Core 查询

[英]Simplify Entity Framework Core query

I do not understand how to simplify this request我不明白如何简化此请求

var dialogs = await dbContext.UsersDialogs
            .AsNoTracking()
            .Where(x => x.UserId == userId)
            .Select(x => new DialogModel
            {
                Id = x.DialogId,
                Login = x.Dialog.Name,
                Image = x.User.FacialImage,
                IsConfirm = x.Dialog.Messages.OrderBy(x => x.DateCreate).LastOrDefault().IsRead,
                DateTime = x.Dialog.Messages.OrderBy(x => x.DateCreate).LastOrDefault().DateCreate,
                LastMessage = x.Dialog.Messages.OrderBy(x => x.DateCreate).LastOrDefault().Content,
                LastUserId = x.Dialog.Messages.OrderBy(x => x.DateCreate).LastOrDefault().UserId
            })
            .ToListAsync();

It will transform it into a request like this它会将其转换为这样的请求

SELECT [u].[DialogId] AS [Id], [d].[Name] AS [Login], [u0].[FacialImage] AS [Image], (
      SELECT TOP(1) [m].[IsRead]
      FROM [Messages] AS [m]
      WHERE [d].[Id] = [m].[DialogId]
      ORDER BY [m].[DateCreate] DESC) AS [IsConfirm], (
      SELECT TOP(1) [m0].[DateCreate] 
  FROM [Messages] AS [m0]
      WHERE [d].[Id] = [m0].[DialogId]
      ORDER BY [m0].[DateCreate] DESC) AS [DateTime], (
      SELECT TOP(1) [m1].[Content]
      FROM [Messages] AS [m1]
      WHERE [d].[Id] = [m1].[DialogId]
      ORDER BY [m1].[DateCreate] DESC) AS [LastMessage], (
      SELECT TOP(1) [m2].[UserId]
      FROM [Messages] AS [m2]
      WHERE [d].[Id] = [m2].[DialogId]
      ORDER BY [m2].[DateCreate] DESC) AS [LastUserId]
  FROM [UsersDialogs] AS [u]
  INNER JOIN [Dialogs] AS [d] ON [u].[DialogId] = [d].[Id]
  INNER JOIN [Users] AS [u0] ON [u].[UserId] = [u0].[Id]
  WHERE [u].[UserId] = @__userId_0

Сan I somehow optimize it? Сan我以某种方式优化它? I don't want to use a SQL query because Linq seems more convenient to me.我不想使用 SQL 查询,因为 Linq 对我来说似乎更方便。

You have created query which is translated directly to the same SQL.您已创建直接转换为相同 SQL 的查询。 You can omit repeating queries by additional Select .您可以通过额外的Select省略重复查询。 Also AsNoTracking is not needed - EF Core do not tack custom entities.也不需要AsNoTracking - EF Core 不添加自定义实体。

var dialogs = await dbContext.UsersDialogs
    .Where(x => x.UserId == userId)
    .Select(x => new 
    { 
        UserDialog = x, 
        LastMessage = x.Dialog.Messages.OrderByDescending(x => x.DateCreate).FirstOrDefault()
    })
    .Select(x => new DialogModel
    {
        Id = x.UserDialog.DialogId,
        Login = x.UserDialog.Dialog.Name,
        Image = x.UserDialog.User.FacialImage,
        IsConfirm = x.LastMessage.IsRead,
        DateTime = x.LastMessage.DateCreate,
        LastMessage = x.LastMessage.Content,
        LastUserId = x.LastMessage.UserId
    })
    .ToListAsync();

As far as the LINQ query is concerned, you can simplify it by using query syntax and let :就 LINQ 查询而言,您可以使用查询语法来简化它, let

from d in dbContext.UsersDialogs
where d.UserId == userId
let lastMessage = d.Dialog.Messages.OrderBy(d => d.DateCreate).LastOrDefault()
select new DialogModel
{
    Id = d.DialogId,
    Login = d.Dialog.Name,
    Image = d.User.FacialImage,
    IsConfirm = lastMessage.IsRead,
    DateTime = lastMessage.DateCreate,
    LastMessage = lastMessage.Content,
    LastUserId = lastMessage.UserId
}

But that doesn't optimize the SQL query.但这并没有优化 SQL 查询。 EF generates the same subquery over and over again for each field from the Messages table. EF 为 Messages 表中的每个字段一遍又一遍地生成相同的子查询。 In SQL Server, the query plan doesn't optimize these subqueries away into one branch.在 SQL 服务器中,查询计划不会将这些子查询优化到一个分支中。

If you really want to optimize the SQL query you have to do something like this:如果您真的想优化 SQL 查询,您必须执行以下操作:

(
    from d in dbContext.UsersDialogs
    where d.UserId == userId
    select new
    {
        Id = d.DialogId,
        Login = d.Dialog.Name,
        Image = d.User.FacialImage,
        LastMessage = (from d.Dialog.Messages
                       orderby d.DateCreate
                       select new
                       {
                           IsConfirm = d.IsRead
                           d.DateCreate,
                           d.Content,
                           d.UserId
                       }).LastOrDefault()
    }
).AsEnumerable()
.Select(x => new DialogModel
{
    Id = x.DialogId,
    Login = x.Dialog.Name,
    Image = x.User.FacialImage,
    LastMessage.IsConfirm,
    LastMessage.DateCreate,
    LastMessage.Content,
    LastMessage.UserId
})

By adding AsEnumerable , which forces client-side evaluation of the last part of the query, EF generates a query with one subquery that uses the ROW_NUMBER() OVER function to get the last message only once.通过添加AsEnumerable ,强制客户端对查询的最后一部分进行评估,EF 生成一个带有一个子查询的查询,该子查询使用ROW_NUMBER() OVER function 只获取最后一条消息一次。 But of course it's quite a hassle to do this.但是,这样做当然很麻烦。 But it may be necessary if the first query suffer considerable performance hits.但是,如果第一个查询遭受相当大的性能影响,则可能有必要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM