Linq通过类数组属性进行过滤

3

我的架构如下:enter image description here

我目前通过valiants获取产品的方法如下:

    products = context.Products
       .Include(x => x.Skus)
       .Include(x => x.ProductVariants)
           .ThenInclude(pv => pv.Option)
       .Include(x => x.ProductVariants)
           .ThenInclude(pv => pv.Value);

现在我正在尝试通过 OptionIdValueId 添加过滤功能。

以下列表包含UI中选择的每个选项的 OptionIdValueId

   List<Filter> filters;

Filter 是

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

更新:使用的类是

    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类有关

通过使用

List<int> ints = new List<int>();
ints.Add(1);

并且

    var vv = context.ProductVariants
        .Where(v => ints.Any(f => f == v.OptionId));

它只是起作用。我应该使用一个表达式还是其他什么?

static Expression<Func<...

2
你尝试过什么?你的引用实际上是无关紧要的。而且,缩写名称是不好的做法,特别是考虑到它们需要的解释性注释。代码应该在可能的情况下自我说明。 - Aluan Haddad
为什么在不使用它的情况下包含Sku? - Caius Jard
你的Filter类为什么有OidVid,但是你声称崩溃的查询中却有f.of.v - Caius Jard
我已经在本地的FilterClass中将Oid重命名为o,将Vid重命名为v。对于造成的任何困惑,我感到抱歉。我刚刚更新了原始实体。 - IAmNotARobot
@CaiusJard 不幸的是它也失败了。 - IAmNotARobot
显示剩余5条评论
3个回答

0

它可能最终会看起来像:

.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

除非您确实需要从Sku、Option和Value中选择字段,否则可以从查询中删除它们;实际上,只有产品和产品变体需要进行过滤。

编辑:

看起来ORM在将linq转换为SQL时遇到了困难。从产品变体开始可能会有所帮助:

context.ProductVariants
  .Where(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.VariantId))

如果成功了,就把你的产品加进去。

谢谢您的努力!您能检查一下更新吗?我遇到了一个翻译错误,不知道该如何修复它。 - IAmNotARobot
1
尝试从产品变体开始,我会进行编辑。 - Caius Jard
看起来错误仍然存在,但至少现在已经缩小了范围。我刚刚更新了我的问题。 - IAmNotARobot
1
你需要发布我们需要重现的所有内容;我无法使用我这里使用EFC5的测试代码重现任何类似的情况。var someList = ...; context.Collection.Where(c => someList.Any(i => ...)) 的模式非常典型,用于生成查询以返回多个项目。 - Caius Jard
似乎错误与过滤器类有关。能否检查更新? - IAmNotARobot
显示剩余2条评论

0

我无法在 .net core 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

创建数据库。

我注意到你在帖子中将SKU中的ProductId和Product中的Ienumerable<Sku>删除了。所以为了使其正常工作,我需要更改模式吗? - IAmNotARobot
我不知道 - 我将其删除,以便迁移可以为我构建数据库,而不会在外键中创建循环依赖路径。如果您的数据库已经存在,并且某种方式没有循环路径,则可能是因为您的数据库可以正常工作。但我认为有点奇怪的是,您既有产品-SKU-变体,又有产品-变体。就好像您无法确定SKU与哪个相关。 - Caius Jard

0

好的,最终使用了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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接