繁体   English   中英

如何根据导航属性 EFCore 执行过滤

[英]How to perform filtering based on navigational properties EFCore

在我的项目中,我有一个 Box 实体:

public class Box : BaseEntity
    {
        public string Name { get; set; }
        public BoxSize Size { get; set; } = BoxSize.Small;
        public BoxColor Color { get; set; } = BoxColor.Black;
        public double Price { get; set; }
        public int Stock { get; set; }
        public List<Product> Products { get; set; }
    }

其中有产品列表作为导航属性:

public class Product : BaseEntity
    {
        public string Name { get; set; }
        ...
        public int ProductTypeId { get; set; }
        public ProductType ProductType { get; set; }
        public int ProductBrandId { get; set; }
        public ProductBrand ProductBrand { get; set; }
        public int? BoxId { get; set; }
        public Box Box { get; set; }
        public int CategoryId { get; set; }
        public Category Category { get; set; }
    }

在过滤盒子时,我必须按品牌、类型或里面的产品类别来过滤它们。 为了过滤,我使用了一个单独的 object ProductParams:

public class ProductParams
    {
        ...
        public int? BrandId { get; set; }
        public int? TypeId { get; set; }
        public int? CategoryId { get; set; }
        public string Sort { get; set; }
        private string _search;
        public string Search
        {
            get => _search;
            set => _search = value.ToLower();
        }
    }

这段代码描述了行动的想法:

            var result = new List<Box>();
            foreach (var box in boxes)
            {
               bool isSearch = false, isType = false, isBrand = false, isCategory = false;
               foreach (var product in box.Products)
               {
                   if (!string.IsNullOrEmpty(productParams.Search))
                   {
                       if (product.Name.ToLower().Contains(productParams.Search.ToLower()))
                           isSearch = true;
                   }
                   else
                       isSearch = true;

                   if (productParams.TypeId != null)
                   {
                       if (product.ProductTypeId == productParams.TypeId)
                           isType = true;
                   }
                   else
                       isType = true;

                   if (productParams.BrandId != null)
                   {
                       if (product.ProductBrandId == productParams.BrandId)
                           isBrand = true;
                   }
                   else
                       isBrand = true;

                   if (productParams.CategoryId != null)
                   {
                       if (product.CategoryId == productParams.CategoryId)
                           isCategory = true;
                   }
                   else
                       isCategory = true;
               }
               if (isSearch && isType && isBrand && isCategory)
                   result.Add(box);

我想知道是否可以将上面的代码实现为 Linq 查询,因为存在双循环。 例如:

var boxes = _context.Boxes
                    .Include(b => b.Products)
                        .ThenInclude(p => p.ProductBrand)
                    .Include(b => b.Products)
                        .ThenInclude(p => p.ProductType)
                    .Include(b => b.Products)
                        .ThenInclude(p => p.Category)
                    .Where(***/// Some code I am looking for using ProductParams///***)
                    .ToList();

是的,您可以使用 LINQKit 库中的PredicateBuilder来做到这一点:

从示意图上看,它应该如下所示:

var query = _context.Boxes
    .Include(b => b.Products)
        .ThenInclude(p => p.ProductBrand)
    .Include(b => b.Products)
        .ThenInclude(p => p.ProductType)
    .Include(b => b.Products)
        .ThenInclude(p => p.Category)
    .AsQueryable();

var predicate = PredicateBuilder.Create<Box>();

if (!string.IsNullOrEmpty(productParams.Search))
    predicate = predicate.And(box => box.Products.Any(product => product.Name.ToLower().Contains(productParams.Search.ToLower())));

if (productParams.TypeId != null)
    predicate = predicate.And(box => box.Products.Any(product => product.ProductTypeId == productParams.TypeId));

if (productParams.BrandId != null)
    predicate = predicate.And(box => box.Products.Any(product => product.ProductBrandId == productParams.BrandId));

if (productParams.CategoryId != null)
    predicate = predicate.And(box => box.Products.Any(product => product.CategoryId == productParams.CategoryId));

var result = query.Where(predicate).ToList();

检查性能,如果它无效,最好通过 GroupBy 来做。 我假设您使用的是 EF Core 5.x:

var query = _context.Boxes
    .Include(b => b.Products)
        .ThenInclude(p => p.ProductBrand)
    .Include(b => b.Products)
        .ThenInclude(p => p.ProductType)
    .Include(b => b.Products)
        .ThenInclude(p => p.Category)
    .AsQueryable();

var grouped = context.Products
    .Where(product => product.BoxId != null)
    .GroupBy(product => product.BoxId)
    .Select(g => new 
    {
        BoxId = g.Key,
        IsSearch = productParams.Search == null || productParams.Search == '' ? true : 
            g.Count(product => product.Name.ToLower().Contains(productParams.Search.ToLower()) > 0, 
        IsType = productParams.TypeId == null ? true : 
            g.Count(product => product.ProductTypeId == productParams.TypeId) > 0, 
        IsBrand = productParams.BrandId == null ? true : 
            g.Count(product => product.ProductBrandId == productParams.BrandId) > 0,
        IsCategory = productParams.CategoryId == null ? true : ;
            g.Count(product => product.CategoryId == productParams.CategoryId) > 0
    })
    .Where(g => g.IsSearch && g.IsType && g.IsBrand && g.IsCategory)

var result = query
    .Join(grouped, box => box.Id, g => g.BoxId, (box, g) => box)
    .ToList()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM