C# 正在寻找一种有效的方法,用Entity Framework Core为每一行检索一组多对多子级

C# 正在寻找一种有效的方法,用Entity Framework Core为每一行检索一组多对多子级,c#,linq,query-optimization,asp.net-core-2.1,entity-framework-core-2.1,C#,Linq,Query Optimization,Asp.net Core 2.1,Entity Framework Core 2.1,上下文:我有一个项目表,每个项目都有一组称为标记的多对多动态描述符。每个标记都属于一个TagType,该TagType确定该标记表示大小、颜色、形状等的信息类型。。每个项目每个标记类型最多只能有一个标记 目标:我希望显示一个包含所有项目的表,并且在每行中都有一列,其中包含标记类型的嵌套表和带有空单元格的标记(如果项目没有该标记类型的标记) 问题:Linq查询至少需要30秒,只需5000项。我希望有10万件以上的物品。我需要找到一种更有效地填充此信息的方法 模型:忽略不相关的属性 public c

上下文:我有一个项目表,每个项目都有一组称为标记的多对多动态描述符。每个标记都属于一个TagType,该TagType确定该标记表示大小、颜色、形状等的信息类型。。每个项目每个标记类型最多只能有一个标记

目标:我希望显示一个包含所有项目的表,并且在每行中都有一列,其中包含标记类型的嵌套表和带有空单元格的标记(如果项目没有该标记类型的标记)

问题:Linq查询至少需要30秒,只需5000项。我希望有10万件以上的物品。我需要找到一种更有效地填充此信息的方法

模型:忽略不相关的属性

public class Item : BaseEntity
{
    public virtual ICollection<ItemTag> ItemTags { get; set; }
}

查询:


最慢的部分似乎是GetDisplayTagsitm.ItemTags.Selectit=>it.Tag.ToList,tts;但是我不知道如何才能获得这些信息。

您在OnGetItemsAsync中的查询不知道DisplayItem构造函数中发生了什么,因此您的查询返回整个Items集合、每个Item的ItemTags集合、每个ItemTag的Tag集合以及每个Tag的TagType集合,所以你带回了很多不必要的数据。要防止它,您应该从查询中删除include。 您还可以优化查询,这在您看来是最慢的部分。FirstOrDefault每次都会在整个集合中搜索马赫数。你可以试试

return tts.Select(tt => new DisplayTag(tt.Name, tags.Where(t=>t.TagTypeId==tt.Id).GroupBy(t=>t.TagTypeId).Select(tg=>tg.First()?.Value??" "))).ToList();

然后查询将返回TagTypeId的集合,而不必在整个集合中搜索第一个条目。

提示1:只返回真正需要的列need@JohnB这就是我使用显示模型的原因。我确实注意到我可以稍微延迟.ToListAsync,并将其放在查询的末尾。不过,这并没有多大区别。基本查询本身的持续时间是多少?也就是说,_context.Items.Include。AsNoTracking@GertArnold如果我在显示模型中用空列表替换对GetDisplayTagsitm.ItemTags.Selectit=>it.Tag.ToList,tts的调用,初始查询只需约2秒。每个项目大约有多少个ItemTags?如果没有,我不确定如何从这些表中获取信息。包括它们?我关闭LazyLoading.AsNoTracking的原因是,创建的代理大大降低了整个过程的速度。至于标记,其中t=>t.TagTypeId==tt.Id.GroupByt=>t.TagTypeId.Selecttg=>tg.First?.Value,我在那里传递的标签已经只是属于该项目的标签,可能是~5个标签。它们已经被保证是唯一的,所以对它们进行分组难道不会增加不必要的开销吗?@Anacronym当您将项传递到DisplayItem构造函数时,它将保留在数据上下文中,因此它将延迟加载需要的任何导航属性。但我认为,最好的解决方案是将查询中的所有查询和选择移动到OnGetItemAsync中,这样EF就可以看到它的所有元素。或者,为了最大限度地优化,将其移动到数据库:编写一个sql函数,该函数只返回所需的一组数据。
public class Tag : BaseEntity
{
    [Required]
    [MaxLength(100)]
    public string Value { get; set; }

    [Required]
    public Guid TagTypeId { get; set; }
    public virtual TagType TagType {get;set;}

    public virtual ICollection<ItemTag> ItemTags { get; set; }
}
public class TagType : BaseEntity
{
    [Required]
    [MaxLength(50)]
    public string Name { get; set; }

    public int Position { get; set; }

    public virtual ICollection<Tag> Tags { get; set; } 
}
public class DisplayItem
{
    public Guid Id { get; set; }
    public List<DisplayTag> ItemTags { get; set; }

    public DisplayItem(Item itm, IList<TagType> tts)
    {
        Id = itm.Id;
        ItemTags = GetDisplayTags(itm.ItemTags
            .Select(it => it.Tag).ToList(), tts);
    }

    public List<DisplayTag> GetDisplayTags(IList<Tag> tags, IList<TagType> tts)
    {
        return tts.Select(tt => new DisplayTag(tt.Name, 
            tags.FirstOrDefault(t => t.TagTypeId == tt.Id)?.Value ?? "  ")).ToList();
    }
}
public async Task<JsonResult> OnGetItemsAsync()
{
    TagTypes = await _context.TagType.OrderBy(tt => tt.Position).AsNoTracking().ToListAsync();
    return new JsonResult(await _context.Items
        .Include(itm => itm.ItemTags)
        .ThenInclude(it => it.Tag)
        .ThenInclude(t => t.TagType)
        .AsNoTracking()
        .Select(itm => new DisplayItem(itm, TagTypes))
        .ToListAsync());
}
return tts.Select(tt => new DisplayTag(tt.Name, tags.Where(t=>t.TagTypeId==tt.Id).GroupBy(t=>t.TagTypeId).Select(tg=>tg.First()?.Value??" "))).ToList();