【问题标题】:Linq filtering by class array propertiesLinq 按类数组属性过滤
【发布时间】:2021-04-22 04:17:21
【问题描述】:

我的架构是

我目前正在与这样的勇士一起购买产品

    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;

过滤器在哪里

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<...

【问题讨论】:

  • 你尝试了什么?您的包含确实无关紧要。此外,像这样缩写名称也是不好的做法,特别是考虑到它们需要的解释性 cmets。代码应尽可能自我记录
  • 不使用的时候为什么要包含Sku?
  • 为什么您的过滤器类有OidVid 但您声称崩溃的查询有f.of.v
  • @我已在本地将 FilterClass 中的 Oid 重命名为 o 和 Vid 重命名为 v。很抱歉产生任何混淆。我刚刚更新了原始实体
  • @CaiusJard sku 应该是返回到 MVC 视图的模型的一部分

标签: c# linq entity-framework-core .net-5


【解决方案1】:

它可能最终看起来像:

.Where(prod => prod.Variants.Any(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.VariantId)))

如果您的 ProductVariants 实体没有 Id(不确定您如何设置实体),您可能需要 f.Oid == v.Option.Id &amp;&amp; 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))

如果可行,请添加您的产品

【讨论】:

  • 感谢您的努力!你能检查一下更新吗?我遇到翻译错误,不知道如何修复它
  • 尝试从产品变体开始,我会进行编辑
  • 似乎错误仍然存​​在,至少现在已缩小范围。刚刚更新了我的问题
  • 你需要发布我们需要的所有东西来复制它;我无法用我在这里使用 EFC5 的测试代码来重现类似的东西。 var someList = ...; context.Collection.Where(c =&gt; someList.Any(i =&gt; ...)) 的模式对于生成查询非常典型,需要返回多个项目
  • 刚刚添加了用于生成数据库的模型
【解决方案2】:

我无法在 .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 并从产品中删除 Ienumerable。所以要完成这项工作,我应该更改架构?
  • 我不知道 - 我删除了它,以便迁移为我构建数据库,而无需在外键中创建循环依赖路径。如果您的数据库已经存在并且不知何故在路径中没有循环,那么这可能就是您的工作方式。我确实认为这有点奇怪,你有 product-sku-variant 和 product-variant。就像您无法决定 sku 与什么有关
【解决方案3】:

好的,最后是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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-10
    • 1970-01-01
    • 1970-01-01
    • 2016-03-30
    • 1970-01-01
    • 2019-05-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多