Linq 按 class 数组属性过滤
Linq filtering by class array properties
我的模式是
我目前正在购买这样的产品
products = context.Products
.Include(x => x.Skus)
.Include(x => x.ProductVariants)
.ThenInclude(pv => pv.Option)
.Include(x => x.ProductVariants)
.ThenInclude(pv => pv.Value);
现在我正尝试通过 OptionId
和 ValueId
添加过滤器功能
下面的列表同时包含 OptionId
和 ValueId
对于在 UI
中选择的每个选项
List<Filter> filters;
过滤器在哪里
public class Filter
{
public int Oid { get; set; } //OptionId
public int Vid { get; set; } //ValueId
}
如何在这个上添加过滤功能?
使用后
var v = context.Products.Include(x => x.ProductVariants)
.Where(prod => prod.ProductVariants
.Any(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.ValueId)));
我收到错误
The LINQ expression 'DbSet<ProductVariant>()
.Where(p0 => EF.Property<Nullable<int>>(EntityShaperExpression:
EntityType: Product
ValueBufferExpression:
ProjectionBindingExpression: EmptyProjectionMember
IsNullable: False
, "Id") != null && object.Equals(
objA: (object)EF.Property<Nullable<int>>(EntityShaperExpression:
EntityType: Product
ValueBufferExpression:
ProjectionBindingExpression: EmptyProjectionMember
IsNullable: False
, "Id"),
objB: (object)EF.Property<Nullable<int>>(p0, "ProductId")))
.Any(p0 => __filters_0
.Any(f => f.o == p0.OptionId && f.v == p0.ValueId))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
更新
使用时
var vv = context.ProductVariants
.Where(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.ValueId)).AsEnumerable();
现在的错误是
The LINQ expression 'DbSet<ProductVariant>()
.Where(p => __filters_0
.Any(f => f.Oid == p.OptionId && f.Vid == p.ValueId))' could not be translated.
更新
即使仅按选项
过滤,错误仍然存在
var vv = context.ProductVariants
.Where(v => filters.Any(f => f.Oid == v.OptionId)).AsEnumerable();
更新使用的class是
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public IEnumerable<ProductVariant> ProductVariants { get; set; }
public IEnumerable<Sku> Skus { get; set; }
}
public enum FilterType
{
CheckBox,
Radio,
Button,
List
}
public class Option
{
public int OptionId { get; set; }
public string OptionName { get; set; }
public FilterType FilterType { get; set; }
}
public class Value
{
public int ValueId { get; set; }
public string OptionValue { get; set; }
}
public class Sku
{
public int SkuId { get; set; }
public int ProductId { get; set; }
public decimal Price { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
}
public class ProductVariant
{
public int Id { get; set; }
public int ProductId { get; set; }
public int OptionId { get; set; }
public int ValueId { get; set; }
public int SkuId { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
[ForeignKey("OptionId")]
public Option Option { get; set; }
[ForeignKey("ValueId")]
public Value Value { get; set; }
[ForeignKey("SkuId")]
public Sku Sku { get; set; }
}
更新
我已经缩小了与 Filter
class
相关的错误
通过使用
List<int> ints = new List<int>();
ints.Add(1);
和
var vv = context.ProductVariants
.Where(v => ints.Any(f => f == v.OptionId));
它很管用。我应该使用表达式还是其他东西?
static Expression<Func<...
它最终可能看起来像:
.Where(prod => prod.Variants.Any(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.VariantId)))
如果您的 ProductVariants 实体没有 ID(不确定您如何设置实体),您可能需要 f.Oid == v.Option.Id && f.Vid == v.Variant.Id
除非您确实需要 select Sku、Option 和 Value 字段,否则它们可以从查询中删除;实际上,过滤只需要产品和产品变体
编辑:
看起来 ORM 正在努力将 linq 变成 SQL。从产品变体开始可能会有所帮助:
context.ProductVariants
.Where(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.VariantId))
如果成功,请将您的产品添加到
我无法在 .net 核心 winforms 应用程序和 ef5 中使用此代码复制问题:
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Linq;
using System.Windows.Forms;
namespace WFNetCoreCSWithEF5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var f = new List<Filter>() { new Filter { Oid = 1, Vid = 2 } };
var x = new ProductDbContext().ProductVariant.Where(pc => f.Any(f => f.Oid == pc.OptionId && f.Vid == pc.ValueId));
}
}
public class Filter
{
public int Oid { get; set; } //OptionId
public int Vid { get; set; } //ValueId
}
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public IEnumerable<ProductVariant> ProductVariants { get; set; }
//public IEnumerable<Sku> Skus { get; set; }
}
public enum FilterType
{
CheckBox,
Radio,
Button,
List
}
public class Option
{
public int OptionId { get; set; }
public string OptionName { get; set; }
public FilterType FilterType { get; set; }
}
public class Value
{
public int ValueId { get; set; }
public string OptionValue { get; set; }
}
public class Sku
{
public int SkuId { get; set; }
//public int ProductId { get; set; }
public decimal Price { get; set; }
//[ForeignKey("ProductId")]
//public Product Product { get; set; }
}
public class ProductVariant
{
public int Id { get; set; }
public int ProductId { get; set; }
public int OptionId { get; set; }
public int ValueId { get; set; }
public int SkuId { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
[ForeignKey("OptionId")]
public Option Option { get; set; }
[ForeignKey("ValueId")]
public Value Value { get; set; }
[ForeignKey("SkuId")]
public Sku Sku { get; set; }
}
public class ProductDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<ProductVariant> ProductVariant { get; set; }
public DbSet<Sku> Skus { get; set; }
public DbSet<Option> Options { get; set; }
public DbSet<Value> Values { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer(@"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=c:\temp\new3.mdf;Integrated Security=True;Connect Timeout=30");
}
}
还有这些 DOS 命令:
dotnet ef migrations add X
dotnet ef database update
创建数据库
好的,结果是 PredicateBuilder
if (filters.Any())
{
var predicate = PredicateBuilder.False<Product>();
foreach (var filter in filters)
predicate = predicate.Or(p => p.ProductVariants.Any(x => x.OptionId == filter.o & x.ValueId == filter.v));
products = products.AsQueryable().Where(predicate);
}
products = products.AsQueryable()
.Include(x => x.ProductVariants)
.ThenInclude(pv => pv.Option)
.Include(x => x.ProductVariants)
.ThenInclude(x => x.Value);
我的模式是
我目前正在购买这样的产品
products = context.Products
.Include(x => x.Skus)
.Include(x => x.ProductVariants)
.ThenInclude(pv => pv.Option)
.Include(x => x.ProductVariants)
.ThenInclude(pv => pv.Value);
现在我正尝试通过 OptionId
和 ValueId
下面的列表同时包含 OptionId
和 ValueId
对于在 UI
List<Filter> filters;
过滤器在哪里
public class Filter
{
public int Oid { get; set; } //OptionId
public int Vid { get; set; } //ValueId
}
如何在这个上添加过滤功能?
使用后
var v = context.Products.Include(x => x.ProductVariants)
.Where(prod => prod.ProductVariants
.Any(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.ValueId)));
我收到错误
The LINQ expression 'DbSet<ProductVariant>()
.Where(p0 => EF.Property<Nullable<int>>(EntityShaperExpression:
EntityType: Product
ValueBufferExpression:
ProjectionBindingExpression: EmptyProjectionMember
IsNullable: False
, "Id") != null && object.Equals(
objA: (object)EF.Property<Nullable<int>>(EntityShaperExpression:
EntityType: Product
ValueBufferExpression:
ProjectionBindingExpression: EmptyProjectionMember
IsNullable: False
, "Id"),
objB: (object)EF.Property<Nullable<int>>(p0, "ProductId")))
.Any(p0 => __filters_0
.Any(f => f.o == p0.OptionId && f.v == p0.ValueId))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
更新 使用时
var vv = context.ProductVariants
.Where(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.ValueId)).AsEnumerable();
现在的错误是
The LINQ expression 'DbSet<ProductVariant>()
.Where(p => __filters_0
.Any(f => f.Oid == p.OptionId && f.Vid == p.ValueId))' could not be translated.
更新 即使仅按选项
过滤,错误仍然存在 var vv = context.ProductVariants
.Where(v => filters.Any(f => f.Oid == v.OptionId)).AsEnumerable();
更新使用的class是
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public IEnumerable<ProductVariant> ProductVariants { get; set; }
public IEnumerable<Sku> Skus { get; set; }
}
public enum FilterType
{
CheckBox,
Radio,
Button,
List
}
public class Option
{
public int OptionId { get; set; }
public string OptionName { get; set; }
public FilterType FilterType { get; set; }
}
public class Value
{
public int ValueId { get; set; }
public string OptionValue { get; set; }
}
public class Sku
{
public int SkuId { get; set; }
public int ProductId { get; set; }
public decimal Price { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
}
public class ProductVariant
{
public int Id { get; set; }
public int ProductId { get; set; }
public int OptionId { get; set; }
public int ValueId { get; set; }
public int SkuId { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
[ForeignKey("OptionId")]
public Option Option { get; set; }
[ForeignKey("ValueId")]
public Value Value { get; set; }
[ForeignKey("SkuId")]
public Sku Sku { get; set; }
}
更新
我已经缩小了与 Filter
class
通过使用
List<int> ints = new List<int>();
ints.Add(1);
和
var vv = context.ProductVariants
.Where(v => ints.Any(f => f == v.OptionId));
它很管用。我应该使用表达式还是其他东西?
static Expression<Func<...
它最终可能看起来像:
.Where(prod => prod.Variants.Any(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.VariantId)))
如果您的 ProductVariants 实体没有 ID(不确定您如何设置实体),您可能需要 f.Oid == v.Option.Id && f.Vid == v.Variant.Id
除非您确实需要 select Sku、Option 和 Value 字段,否则它们可以从查询中删除;实际上,过滤只需要产品和产品变体
编辑:
看起来 ORM 正在努力将 linq 变成 SQL。从产品变体开始可能会有所帮助:
context.ProductVariants
.Where(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.VariantId))
如果成功,请将您的产品添加到
我无法在 .net 核心 winforms 应用程序和 ef5 中使用此代码复制问题:
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Linq;
using System.Windows.Forms;
namespace WFNetCoreCSWithEF5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var f = new List<Filter>() { new Filter { Oid = 1, Vid = 2 } };
var x = new ProductDbContext().ProductVariant.Where(pc => f.Any(f => f.Oid == pc.OptionId && f.Vid == pc.ValueId));
}
}
public class Filter
{
public int Oid { get; set; } //OptionId
public int Vid { get; set; } //ValueId
}
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public IEnumerable<ProductVariant> ProductVariants { get; set; }
//public IEnumerable<Sku> Skus { get; set; }
}
public enum FilterType
{
CheckBox,
Radio,
Button,
List
}
public class Option
{
public int OptionId { get; set; }
public string OptionName { get; set; }
public FilterType FilterType { get; set; }
}
public class Value
{
public int ValueId { get; set; }
public string OptionValue { get; set; }
}
public class Sku
{
public int SkuId { get; set; }
//public int ProductId { get; set; }
public decimal Price { get; set; }
//[ForeignKey("ProductId")]
//public Product Product { get; set; }
}
public class ProductVariant
{
public int Id { get; set; }
public int ProductId { get; set; }
public int OptionId { get; set; }
public int ValueId { get; set; }
public int SkuId { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
[ForeignKey("OptionId")]
public Option Option { get; set; }
[ForeignKey("ValueId")]
public Value Value { get; set; }
[ForeignKey("SkuId")]
public Sku Sku { get; set; }
}
public class ProductDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<ProductVariant> ProductVariant { get; set; }
public DbSet<Sku> Skus { get; set; }
public DbSet<Option> Options { get; set; }
public DbSet<Value> Values { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer(@"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=c:\temp\new3.mdf;Integrated Security=True;Connect Timeout=30");
}
}
还有这些 DOS 命令:
dotnet ef migrations add X
dotnet ef database update
创建数据库
好的,结果是 PredicateBuilder
if (filters.Any())
{
var predicate = PredicateBuilder.False<Product>();
foreach (var filter in filters)
predicate = predicate.Or(p => p.ProductVariants.Any(x => x.OptionId == filter.o & x.ValueId == filter.v));
products = products.AsQueryable().Where(predicate);
}
products = products.AsQueryable()
.Include(x => x.ProductVariants)
.ThenInclude(pv => pv.Option)
.Include(x => x.ProductVariants)
.ThenInclude(x => x.Value);