Ищете эффективный способ получения набора потомков «многие ко многим» для каждой строки с помощью Entity Framework Core - PullRequest
0 голосов
/ 13 декабря 2018

Контекст: У меня есть таблица Items, каждая из которых имеет динамическое множество дескрипторов, называемых Tags.Каждый Tag принадлежит TagType, который определяет, какую информацию представляет Tag (размер, цвет, форма и т. Д.).Каждый Item может иметь максимум один Tag на TagType.

Цель: Я хотел бы отобразить таблицу всех моих Items и иметьстолбец в каждой строке с вложенной таблицей <th>TagTypes</th> и <td>Tags</td> с пустыми ячейками, если Item не имеет Tag для этого TagType.

Проблема: Запрос Linq занимает не менее 30 секунд всего с 5000 Items.Я ожидаю получить 100k + Items.Мне нужно найти способ более эффективного заполнения этой информации.


Модели : (без учета несвязанных свойств)

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

 public class ItemTag
{
    public Guid ItemId { get; set; }
    public virtual Item  Item {get; set;}

    public Guid TagId { get; set; }
    public virtual Tag Tag { get; set; }
}

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; } 
}

Затем у меня есть модель дисплея DisplayItem, которую я конвертирую в Json и возвращаю.

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());
}

Самая медленная часть, кажется, GetDisplayTags(itm.ItemTags.Select(it => it.Tag).ToList(), tts);, но я не уверен, как еще я мог бы получить эту информацию.

1 Ответ

0 голосов
/ 13 декабря 2018

Ваш запрос в OnGetItemsAsync не знает, что происходит в конструкторе DisplayItem, поэтому ваш запрос возвращает всю коллекцию Items, коллекцию ItemTags для каждого Item и коллекцию Tag для каждого ItemTag и коллекцию TagType для каждого Tag, поэтомуВы возвращаете много ненужных данных.Чтобы предотвратить это, вы должны удалить включенные из вашего запроса.Также вы можете оптимизировать запрос, который кажется вам самой медленной частью.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 и не будет искать во всей коллекции первую запись.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...