gpt4 book ai didi

c# - 使用 linq 和 Entity Framework 构建嵌套表达式

转载 作者:太空狗 更新时间:2023-10-29 23:12:10 25 4
gpt4 key购买 nike

我正在尝试创建一个基于过滤器返回目录的服务。

我在互联网上看到了一些结果,但不完全是我的问题。我希望你能帮助我。

问题是此查询构建无法转换为存储表达式:

'LINQ to Entities does not recognize the method 'System.Linq.IQueryable'1[App.Data.Models.Subgroup] HasProductsWithState[Subgroup](System.Linq.IQueryable'1[App.Data.Models.Subgroup], System.Nullable`1[System.Boolean])' method, and this method cannot be translated into a store expression.'



我如何才能将查询转换为商店表达式。
请不要建议 .ToList()作为答案,因为我不希望它在内存中运行。

所以我所拥有的是:

    bool? isActive = null;
string search = null;

DbSet<Maingroup> query = context.Set<Maingroup>();

var result = query.AsQueryable()
.HasProductsWithState(isActive)
.HasChildrenWithName(search)
.OrderBy(x => x.SortOrder)
.Select(x => new CatalogViewModel.MaingroupViewModel()
{
Maingroup = x,
Subgroups = x.Subgroups.AsQueryable()
.HasProductsWithState(isActive)
.HasChildrenWithName(search)
.OrderBy(y => y.SortOrder)
.Select(y => new CatalogViewModel.SubgroupViewModel()
{
Subgroup = y,
Products = y.Products.AsQueryable()
.HasProductsWithState(isActive)
.HasChildrenWithName(search)
.OrderBy(z => z.SortOrder)
.Select(z => new CatalogViewModel.ProductViewModel()
{
Product = z
})
})
});

return new CatalogViewModel() { Maingroups = await result.ToListAsync() };

在下面的代码中,您可以看到我递归调用扩展来尝试堆叠表达式。但是当我在运行时遍历我的代码时,它不会再次进入该函数

    return maingroups.Where(x => x.Subgroups.AsQueryable().HasProductsWithState(state).Any()) as IQueryable<TEntity>;

叫做。

    public static class ProductServiceExtensions
{
public static IQueryable<TEntity> HasProductsWithState<TEntity>(this IQueryable<TEntity> source, bool? state)
{
if (source is IQueryable<Maingroup> maingroups)
{
return maingroups.Where(x => x.Subgroups.AsQueryable().HasProductsWithState(state).Any()) as IQueryable<TEntity>;
}
else if (source is IQueryable<Subgroup> subgroups)
{
return subgroups.Where(x => x.Products.AsQueryable().HasProductsWithState(state).Any()) as IQueryable<TEntity>;
}
else if (source is IQueryable<Product> products)
{
return products.Where(x => x.IsActive == state) as IQueryable<TEntity>;
}

return source;
}

public static IQueryable<TEntity> HasChildrenWithName<TEntity>(this IQueryable<TEntity> source, string search)
{
if (source is IQueryable<Maingroup> maingroups)
{
return maingroups.Where(x => search == null || x.Name.ToLower().Contains(search) || x.Subgroups.AsQueryable().HasChildrenWithName(search).Any()) as IQueryable<TEntity>;
}
else if (source is IQueryable<Subgroup> subgroups)
{
return subgroups.Where(x => search == null || x.Name.ToLower().Contains(search) || x.Products.AsQueryable().HasChildrenWithName(search).Any()) as IQueryable<TEntity>;
}
else if (source is IQueryable<Product> products)
{
return products.Where(x => search == null || x.Name.ToLower().Contains(search)) as IQueryable<TEntity>;
}

return source;
}
}

更新

缺课:

    public class Maingroup
{
public long Id { get; set; }
public string Name { get; set; }
...
public virtual ICollection<Subgroup> Subgroups { get; set; }
}

    public class Subgroup
{
public long Id { get; set; }
public string Name { get; set; }

public long MaingroupId { get; set; }
public virtual Maingroup Maingroup { get; set; }
...
public virtual ICollection<Product> Products { get; set; }
}

    public class Product
{
public long Id { get; set; }
public string Name { get; set; }

public long SubgroupId { get; set; }
public virtual Subgroup Subgroup { get; set; }
...
public bool IsActive { get; set; }
}

最佳答案

你的问题的原因
您必须了解 IEnumerable 和 IQueryable 之间的关系。一个 IEnumerable 对象包含了枚举所有元素的所有内容:您可以请求序列的第一个元素,一旦获得一个元素,您就可以请求下一个元素,直到没有更多元素为止。
IQueryable 看起来很相似,但是,IQueryable 并不包含枚举序列的所有内容。它拥有一个 Expression和一个 Provider . Expression是必须查询的通用形式。 Provider知道谁必须执行查询(通常是一个数据库管理系统)、如何与这个执行器通信以及使用哪种语言(通常是类似 SQL 的东西)。
一旦你开始枚举,要么通过调用 GetEnumerator 显式调用和 MoveNext ,或通过调用 foreach 隐式调用, ToList , FirstOrDefault , Count等,Expression发送至 Provider ,谁将把它翻译成 SQL 并调用 DBMS。返回的数据呈现为一个 IEnumerable 对象,该对象被枚举,使用 GetEnumerator因为提供商必须翻译 Expression进入SQL,Expression只能调用可以转换为 SQL 的函数。唉,Provider不知道HasProductsWithState ,也没有您自己定义的任何函数,因此无法将其转换为 SQL。事实上, Entity Framework 提供者也不知道如何翻译几个标准的 LINQ 函数,因此它们不能使用 AsQueryable .见 Supported and Unsupported LINQ methods .
因此,您必须坚持使用返回 IQueryable 的函数,其中表达式仅包含受支持的函数。
类(class)说明
唉,你忘了给我们你的实体类,所以我不得不对它们做一些假设。
显然有一个至少包含三个 DbSet 的 DbContext:MainGroups , SubGroupsProducts .MaingGroups 之间似乎存在一对多(或可能的多对多)关系。和 SubGroups : 每 MainGroup有零个或多个 SubGroups .
看来SubGroups之间也存在一对多的关系和 Products : 每 SubGroup有零个或多个 Products .
唉,你忘了提到返回关系:是否每 Product完全属于一个 SubGroup (一对多),或每隔 Product 执行一次属于零个或多个 SubGroups (多对多`)?
如果您遵循 Entity Framework 代码优先约定,您将拥有与此类似的类:

class MainGroup
{
public int Id {get; set;}
...

// every MainGroup has zero or more SubGroups (one-to-many or many-to-many)
public virtual ICollection<SubGroup> SubGroups {get; set;}
}
class SubGroup
{
public int Id {get; set;}
...

// every SubGroup has zero or more Product(one-to-many or many-to-many)
public virtual ICollection<Product> Products{get; set;}

// alas I don't know the return relation
// one-to-many: every SubGroup belongs to exactly one MainGroup using foreign key
public int MainGroupId {get; set;}
public virtual MainGroup MainGroup {get; set;}
// or every SubGroup has zero or more MainGroups:
public virtual ICollection<MainGroup> MainGroups {get; set;}
}
产品类似的东西:
class Product
{
public int Id {get; set;}
public bool? IsActive {get; set;} // might be a non-nullable property
...

// alas I don't know the return relation
// one-to-many: every Productbelongs to exactly one SubGroup using foreign key
public int SubGroupId {get; set;}
public virtual SubGroup SubGroup {get; set;}
// or every Product has zero or more SubGroups:
public virtual ICollection<SubGroup> SubGroups {get; set;}
}
当然还有你的 DbContext:
class MyDbContext : DbContext
{
public DbSet<MainGroup> MainGroups {get; set;}
public DbSet<SubGroup> SubGroups {get; set;}
public DbSet<Product> Products {get; set;}
}
这就是 Entity Framework 检测表、表中的列以及表之间的关系(一对多、多对多、一对零或一)所需知道的全部内容。只有当您想偏离标准命名时,您才需要 fluent API 的属性。

In entity framework the columns of the tables are represented by non-virtual properties. The virtual properties represent the relations between the tables (one-to-many, many-to-many).


请注意,虽然 SubGroupsMainGroup被声明为一个集合,如果你查询 SubGroupsMaingGroup with Id 10你仍然会得到一个 IQueryable。
要求
给定一个可查询的序列 Products和一个可为空的 bool 值 State , HasProductsWithState(products, state)应该返回 Products 的可查询序列值为 IsActive等于 State给定一个可查询的序列 SubGroups和一个可为空的 bool 值 State , HasProductsWithState(subGroups, state)应该返回 SubGroups 的可查询序列至少有一个 Product那个 "HasProductsWithState(Product, State)1
给定一个可查询的序列 MainGroups和一个可为空的 bool 值 State , HasProductsWithState(mainGroups, state)应该返回 MainGroups 的可查询序列, 包含所有 MainGroups至少有一个 SubGroup那个 HasProductsWithState(SubGroup, State)解决方案
好吧,如果你这样写需求,扩展方法很简单:
IQueryable<Product> WhereHasState(this IQueryable<Product> products, bool? state)
{
return products.Where(product => product.IsActive == state);
}
因为这个函数不检查一个产品是否有这个状态,而是返回所有有这个状态的产品,所以我选择使用不同的名称。
bool HasAnyWithState(this IQueryable<Product> products, bool? state)
{
return products.WhereHasState(state).Any();
}
如果 IsActive 是不可为空的属性,您的代码将略有不同。
我会用 SubGroups 做类似的事情:
IQueryable<SubGroup> WhereAnyProductHasState(this IQueryable<SubGroup> subGroups, bool? state)
{
return subgroups.Where(subGroup => subGroup.Products.HasAnyWithState(state));
}

bool HasProductsWithState(this IQueryable<SubGroup> subGroups, bool? state)
{
return subGroups.WhereAnyProductHasState(state).Any();
}
好吧,你现在应该知道 MainGroups 的练习了。 :
IQueryable<MainGroup> WhereAnyProductHasState(this IQueryable<MainGroup> mainGroups, bool? state)
{
return maingroups.Where(mainGroup => mainGroup.SubGroups.HasProductsWithState(state));
}

bool HasProductsWithState(this IQueryable<MainGroup> mainGroups, bool? state)
{
return mainGroups.WhereAnyProductHasState(state).Any();
}
如果你仔细观察,你会发现我没有使用任何自定义函数。我的函数调用只会改变 Expression .变了 Expression可以翻译成SQL。
我把函数拆分成很多小函数,因为你没说要不要用 HasProductsWithState(this IQueryable<SubGroup>, bool?)HasProductsWithState(this IQueryable<Product>, bool?) .
TODO:为 HasChildrenWithName 做类似的事情: 分成更小的函数,只包含 LINQ 函数,没有别的
如果您只拨打 HasProductsWithState(this IQueryable<MainGroup>, bool?)您可以使用“SelectMany”在一个函数中完成此操作:
IQueryable<MainGroup> HasProductsWithState(this IQueryable<MainGroup> mainGroups, bool? state)
{
return mainGroups
.Where(mainGroup => mainGroup.SelectMany(mainGroup.SubGroups)
.SelectMany(subGroup => subGroup.Products)
.Where(product => product.IsActive == state)
.Any() );
}

关于c# - 使用 linq 和 Entity Framework 构建嵌套表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55620681/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com