EF Core:细分IQueryable以便仅重用和加载所选属性的策略

时间:2019-07-14 14:32:29

标签: entity-framework-core iqueryable

我正在寻找一种有效地生成和重用Entity Framework Core中(相当大的)查询的一部分的方法。

我的目标如下:

  1. 生成高效的SQL;包括仅返回列 相关子表中所需的数据,并避免出现N + 1种情况
  2. 抽象并分离从子孙级及其以下(然后的ThenInclude()的第一级)运行的可查询部分,因为该部分查询将用于具有共同孙子级的其他父级实体子实体,我想保留代码DRY。

我当前的查询看起来像这样(简化的)示例:

var itemDetailsQuery = getQueryable(x => x.Id == itemId);
var item = await itemDetailsQuery.FirstOrDefaultAsync();

private IQueryable<Item> getQueryable(Expression<Func<Item,bool>> predicate)

    {
        return _itemRepository
        .GetAll()
        .Where(predicate)
        .Include(x => x.ItemChildren)
            .ThenInclude(x => x.ItemGrandChildren)
                .ThenInclude(x => x.ItemGreatGrandChildren1)

        .Include(x => x.ItemChildren)
            .ThenInclude(x => x.ItemGrandChildren)
                .ThenInclude(x => x.ItemGreatGrandChildren2)

        .Include(x => x.ItemChildren)
            .ThenInclude(x => x.ItemGrandChildren)
                .ThenInclude(x => x.ItemGreatGrandChildren3)
                    .ThenInclude(x => x.ItemGreatGreatGrandChildren)
                        .ThenInclude(x => x.ItemGreatGreatGreatGrandChildren);
    }

以上方法有效,但:

  1. 从相关行/实体中加载每一列,因此效率低下
  2. 如果要从Item以外的父实体类型加载ItemGrandChildren类型及以下类型的实体,我必须重复查询代码。

我发现下面的帖子非常有趣,建议使用表达式和投影来实现所需的结果,这很整洁,因为我可以在每个类中将表达式用作投影:

https://benjii.me/2018/01/expression-projection-magic-entity-framework-core/

我对此的实现如下,每个子类都有自己的投影,因此使用此策略可以将多个级别链接在一起(请注意,ToList()是必需的,因为我的集合属性是ICollections而不是IEnumerables;尽管我担心这可能会导致过早执行:

private IQueryable<Item> getQueryable(Expression<Func<Item,bool>> predicate)
{
    return _itemRepository
    .GetAll()
    .AsExpandable()
    .Where(predicate)
    .Select(x => new Item
    {
        Id = x.Id,
        Name = x.Name,
        ItemChildren = x.ItemChildren.AsQueryable().Select(ItemChild.Projection).ToList(),
    });
}

但是,当我尝试此操作时,经过第二层子级后,什么也没有加载;可能不是设计成多层嵌套使用链式方式。

我已经研究了包括扩展方法在内的替代策略,但是尽管我可以用它们替代第一层的Includes,但是我找不到一种方法来替代这些方法,因此我可以执行以下操作:

    private IQueryable<Item> getQueryable(Expression<Func<Item,bool>> predicate)
    {
        return _itemRepository
        .GetAll()
        .Where(predicate)
        .Include(x => x.ItemChildren)
            .IncludeGrandChildren();
    }

    private IIncludableQueryable<ItemGrandChildren, ItemGreatGrandChildren3> IncludeGrandChildren(this IQueryable<ItemGrandChildren> values)
    {
        return values
            .Include(x => x.ItemGreatGrandChildren1)
            .Include(x => x.ItemGreatGrandChildren2)
            .Include(x => x.ItemGreatGrandChildren3)
                .ThenInclude(x => x.ItemGreatGreatGrandChildren)
                    .ThenInclude(x => x.ItemGreatGreatGreatGrandChildren);
    }

我感觉到我缺少或误解了一些基本知识,但是任何建议都将不胜感激。

0 个答案:

没有答案