Entity framework core EF核心:可重复使用的IQueryable细分策略;仅加载选定属性

Entity framework core EF核心:可重复使用的IQueryable细分策略;仅加载选定属性,entity-framework-core,iqueryable,Entity Framework Core,Iqueryable,我正在实体框架核心中寻找一种高效地生成和重用部分(相当大)查询的方法 我的目标如下: 生成高效的SQL;包括仅返回列 需要从相关子表中删除,并避免N+1场景 抽象并分离从grand child级别及其以下运行的queryable部分(thenClude()的第一个级别),因为查询的这部分将用于具有公共grand child实体的其他父级实体,我希望保持代码干燥 我当前的查询类似于以下(简化)示例: var itemtailsquery=getQueryable(x=>x.Id==itemId);

我正在实体框架核心中寻找一种高效地生成和重用部分(相当大)查询的方法

我的目标如下:

  • 生成高效的SQL;包括仅返回列 需要从相关子表中删除,并避免N+1场景
  • 抽象并分离从grand child级别及其以下运行的queryable部分(thenClude()的第一个级别),因为查询的这部分将用于具有公共grand child实体的其他父级实体,我希望保持代码干燥
  • 我当前的查询类似于以下(简化)示例:

    var itemtailsquery=getQueryable(x=>x.Id==itemId);
    var item=await itemDetailsQuery.FirstOrDefaultAsync();
    私有IQueryable getQueryable(表达式谓词)
    {
    return\u itemRepository
    .GetAll()
    .Where(谓词)
    .Include(x=>x.ItemChildren)
    .然后包括(x=>x.items孙子)
    .然后包括(x=>x.itemGreat孙子1)
    .Include(x=>x.ItemChildren)
    .然后包括(x=>x.items孙子)
    .然后包括(x=>x.Item曾孙2)
    .Include(x=>x.ItemChildren)
    .然后包括(x=>x.items孙子)
    .然后包括(x=>x.Item曾孙3)
    .然后包括(x=>x.Item曾孙)
    。然后包括(x=>x.items曾孙子女);
    }
    
    上述方法可行,但:

  • 从相关行/实体加载每一列,因此效率低下
  • 如果要从Item以外的父实体类型加载Item孙辈及以下类型的实体,则必须复制查询代码
  • 我发现下面的帖子非常有趣,建议使用表达式和投影来实现我想要的结果,这非常简洁,因为我可以在每个类中利用表达式作为投影:

    我的实现如下所示,每个子类都有自己的投影,因此使用此策略将多个级别链接在一起(注意,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(),
        });
    }
    
    private IQueryable getQueryable(表达式谓词)
    {
    return\u itemRepository
    .GetAll()
    .AsExpandable()
    .Where(谓词)
    .选择(x=>新项目
    {
    Id=x.Id,
    Name=x.Name,
    ItemChildren=x.ItemChildren.AsQueryable().Select(ItemChild.Projection).ToList(),
    });
    }
    
    然而,当我尝试这个方法时,经过第二层子对象,没有加载任何内容;这可能是因为它没有被设计为以链式方式使用多层嵌套

    我已经研究了其他策略,包括扩展方法,但尽管我可以让这些策略取代第一层包含,但我无法找到让这些策略取代第二层包含的方法,因此我可以执行以下操作:

        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);
        }
    
    private IQueryable getQueryable(表达式谓词)
    {
    return\u itemRepository
    .GetAll()
    .Where(谓词)
    .Include(x=>x.ItemChildren)
    .IncludeGrandChildren();
    }
    私有IIIncludableQueryable IncludeGrandChildren(此IQueryable值)
    {
    返回值
    .Include(x=>x.itemGreat孙子1)
    .Include(x=>x.itemGreat孙子2)
    .Include(x=>x.item曾孙3)
    .然后包括(x=>x.Item曾孙)
    。然后包括(x=>x.items曾孙子女);
    }
    

    我觉得我遗漏了或误解了一些基本的东西,但任何建议都将不胜感激。

    ,AutoMapper等-所有这些都在某种程度上解决了相同的问题。@IvanStoev,谢谢你的提示。我简要地看了AutoMapper Queryable扩展,并对其进行了折扣,因为我想对其进行更多的控制r进程,尽管.ProjectTo()语法非常吸引人。我将进一步研究NeinLinq和LinqKit,AutoMapper等。-所有这些都在某种程度上解决了相同的问题。@IvanStoev,谢谢你的提示。我简要地看了一下AutoMapper可查询扩展,并对其进行了打折,因为我想对过程进行更多的控制,尽管.ProjectTo()语法非常吸引人。我将进一步调查NeinLinq和LinqKit。
        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);
        }