【问题标题】:Very slow query, how can I improve it?查询速度很慢,如何改进?
【发布时间】:2016-07-20 03:18:46
【问题描述】:

我有以下抽象类:

public abstract class Notification
{
    public Notification()
    {
        this.receivedDate = DateTime.Now.ToUniversalTime();
    }

    public int Id { get; set; }

    public DateTime receivedDate { get; set; }

    public bool unread { get; set; }

    public virtual ApplicationUser recipient { get; set; }
}

有几个类继承自它,例如ProfileViewNotificationNewMessageNotification

public class ProfileViewNotification: Notification
{
    public virtual ApplicationUser Viewer { get; set; }
}

public class NewMessageNotification: Notification
{
    public virtual Message Message { get; set; }
}

我有以下方法来查询我的数据库中的所有Notification,并将给定的ApplicationUser 作为recipient

public static List<NotificationApiViewModel> GetNotificationsForUser(string idOfUser)
    {
        List<NotificationApiViewModel> resultAsApiViewModel = new List<NotificationApiViewModel>();

        List<ProfileViewNotification> ofProfileViewNotificationType = null;
        List<NewMessageNotification> ofNewMessageNotificationType = null;

        try
        {
            using (var context = new ApplicationDbContext())
            {
                var query = context.Notifications.Where(c => c.recipient.Id == idOfUser);

                ofNewMessageNotificationType = query.OfType<NewMessageNotification>().Any() ? 
                    query.OfType<NewMessageNotification>()
                    .Include(n => n.recipient)
                    .Include(n => n.recipient.MyProfile)
                    .Include(n => n.recipient.MyProfile.ProfileImages)
                    .Include(n => n.Message)
                    .Include(n => n.Message.Author)
                    .Include(n => n.Message.Author.MyProfile)
                    .Include(n => n.Message.Author.MyProfile.ProfileImages)
                    .Include(n => n.Message.Recipient)
                    .Include(n => n.Message.Recipient.MyProfile)
                    .Include(n => n.Message.Recipient.MyProfile.ProfileImages)
                    .ToList() 
                    : null;

                ofProfileViewNotificationType = query.OfType<ProfileViewNotification>().Any() ? 
                    query.OfType<ProfileViewNotification>()
                    .Include(n => n.recipient)
                    .Include(n => n.recipient.MyProfile)
                    .Include(n => n.recipient.MyProfile.ProfileImages)
                    .Include(n => n.Viewer)
                    .Include(n => n.Viewer.MyProfile)
                    .Include(n => n.Viewer.MyProfile.ProfileImages)
                    .ToList() 
                    : null;

                }

        }
        catch (Exception ex)
        {
            //Log issue
        }

        if (ofNewMessageNotificationType != null)
        {
            foreach (var n in ofNewMessageNotificationType)
            {
                resultAsApiViewModel.Add(NotificationApiViewModel.ConvertToApiViewModel(n));
            }
        }

        if (ofProfileViewNotificationType != null)
        {
            foreach (var n in ofProfileViewNotificationType)
            {
                resultAsApiViewModel.Add(NotificationApiViewModel.ConvertToApiViewModel(n));
            }
        }

        return resultAsApiViewModel;
    }

需要注意的是,我的ConvertToApiViewModel 方法都没有查询数据库,这就是为什么我在原始查询中包含所有这些Include。此外,为简洁起见,以上仅包括两种类型的通知,但我总共有十几种。

我的问题是我的方法非常慢。对于只有 20 条通知的用户,需要一分钟多的时间才能完成!

谁能告诉我我做错了什么?

【问题讨论】:

  • 我想你应该看看生成的 sql 查询并分析它们,看看你是否缺少一些索引。
  • @KiNeTiC 感谢您的评论。你能指导我如何做到这一点吗?我不知道如何查看生成的 sql 查询,我不确定您所说的“缺少一些索引”是什么意思
  • 对于像这样的复杂查询(很多包含),我建议您改用 LINQ,因为您可以轻松选择所需的字段...您在这里所做的实际上是从数据库中获取所有内容,即远非最佳。
  • 问题是嵌套include的数量,它在sql查询中生成了太多joins。我认为没有 EF 解决方法,所以你应该使用 sql 查询。看看stackoverflow.com/questions/2086894/optimizing-multiple-joins
  • 如何从 EF6 获取生成的查询:stackoverflow.com/questions/1412863/…

标签: c# asp.net-mvc entity-framework


【解决方案1】:

您正在为十几个查询中的每一个调用数据库,因此您可以做的是将它们组合成一个查询,如下所示:

  try
            {
                using (var context = new ApplicationDbContext())
                {
                    //Here you execute the single query
                    var query = context.Notifications.Where(c => c.recipient.Id == idOfUser)
 .Include(n => n.recipient)
                        .Include(n => n.recipient.MyProfile)
                        .Include(n => n.recipient.MyProfile.ProfileImages)
                        .Include(n => n.Message)
                        .Include(n => n.Message.Author)
                        .Include(n => n.Message.Author.MyProfile)
                        .Include(n => n.Message.Author.MyProfile.ProfileImages)
                        .Include(n => n.Message.Recipient)
                        .Include(n => n.Message.Recipient.MyProfile)
                        .Include(n => n.Message.Recipient.MyProfile.ProfileImages)
.Include(n => n.Viewer)
                        .Include(n => n.Viewer.MyProfile)
                        .Include(n => n.Viewer.MyProfile.ProfileImages)
.ToList(); 

                    ofNewMessageNotificationType = query.OfType<NewMessageNotification>().Any() ? 
                        query.OfType<NewMessageNotification>(): null;

                    ofProfileViewNotificationType = query.OfType<ProfileViewNotification>().Any() ? 
                        query.OfType<ProfileViewNotification>() : null;

                    }

            }
            catch (Exception ex)
            {
                //Log issue
            }

            if (ofNewMessageNotificationType != null)
            {
                foreach (var n in ofNewMessageNotificationType)
                {
                    resultAsApiViewModel.Add(NotificationApiViewModel.ConvertToApiViewModel(n));
                }
            }

            if (ofProfileViewNotificationType != null)
            {
                foreach (var n in ofProfileViewNotificationType)
                {
                    resultAsApiViewModel.Add(NotificationApiViewModel.ConvertToApiViewModel(n));
                }
            }

            return resultAsApiViewModel;
        }

希望这会有所帮助...

【讨论】:

  • 对不起,我认为你不能这样做,如果你想使用Include,你需要再次查询,就我而言,在使用ToList() 之后不能再使用Include知道,因为执行Include 是为了查询原始查询首先没有获取的虚拟实体。
  • 您是对的,但您可以做的是将所有包含在单个查询中。我更新了我的答案
  • 很公平,让我试试然后再回复你
  • 这个答案对我来说没有多大意义。我不明白这如何使查询更快。
  • @ShaiAharoni 我认为他的意思是他不能再使用 Include 了,因为基本类型没有他需要加入的类型。我在这里找到了类似的东西stackoverflow.com/questions/6263288/… 并且仍然是一个开放的请求github.com/aspnet/EntityFramework/issues/3910
【解决方案2】:

感谢大家的 cmets 和回复。作为记录,我使查询更快的方式是使用query.Select(n =&gt; new DTO{})Data Transfer Objects (DTOs) 而不是我的多个Include。就这样,我将性能提高了一个数量级。我还将查询设为异步,这进一步提高了性能。

【讨论】:

    猜你喜欢
    • 2023-03-13
    • 1970-01-01
    • 2016-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-14
    • 1970-01-01
    相关资源
    最近更新 更多