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

现在我正尝试通过 OptionIdValueId

添加过滤器功能

下面的列表同时包含 OptionIdValueId 对于在 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);