我有一个非常简单的案例,带有 Controller 和存储库。
Controller :
[HttpGet]
public async Task<IActionResult> GetProductList(ProductQuery queryparams)
{
var products = await uow.ProductRepo.GetProductsWithQuery(queryparams);
var productsToReturn = mapper.Map<IEnumerable<ProductForListDto>>(products);
return Ok(productsToReturn);
}
存储库:
public async Task<AbstractPagedList<Product>>GetProductsWithQuery(ProductQuery qp)
{
var products = DorianContext.Products
.Include(p => p.Category)
.Include(p => p.PriceOffers)
.AsQueryable();
// if (filter.CategoryId.HasValue)
// products = products.Where(p => p.CategoryId == filter.CategoryId);
// if (filter.MinPrice.HasValue)
// products = products.Where(p => p.Price >= filter.MinPrice);
// if (filter.MaxPrice.HasValue)
// products = products.Where(p => p.Price <= filter.MaxPrice);
return await PagedList<Product>.CreateAsync(products, qp.PageNumber, qp.PageSize);
}
型号:
public class ProductQuery
{
public int? CategoryId { get; set; }
public decimal? MinPrice { get; set; }
public decimal? MaxPrice { get; set; }
}
我们如何构建一个动态/通用逻辑来过滤 CategoryId、MinPrice 和 MaxPrice,而不是无聊的注释部分。 (例如在 ProductQuery 的属性列表的 foreach block 中)
也许我们可以使用字典对象和 foreach ,如下所示,但我不太确定如何从对象中获取属性名称作为字符串(我尝试使用 NewtonSoft.JObject 但没有成功)
var filterMap = new Dictionary<string, Expression<Func<Product, bool>>>()
{
["categoryId"] = (v => v.CategoryId == filter.CategoryId),
["collectionId"] = (v => v.ProductCollectionId == filter.CollectionId),
["minPrice"] = (v => v.Price >= filter.MinPrice),
["maxPrice"] = (v => v.Price <= filter.MaxPrice)
};
foreach (var key in filterMap)
{
products = products.Where(key.Value);
}
我不想使用反射。对于此类案例的最佳实践的想法或评论也将受到赞赏。
最佳答案
What I did works yes and I can continue just like this but this will result lots of duplicated logic. And because this is a toy project, I am searching for the ways to improve it. And such a project, this is overkill I agree..
因此,避免破坏 DRY 原则的最佳方法是在 ProductQuery
类中创建一个 Filters
属性,如下所示:
public class ProductQuery
{
public int? CategoryId { get; set; }
public decimal? MinPrice { get; set; }
public decimal? MaxPrice { get; set; }
public IEnumerable<Expression<Func<Product, bool>>> Filters
{
get
{
var filters = new List<Expression<Func<Product, bool>>>();
if (this.CategoryId.HasValue)
filters.Add(p => p.CategoryId == this.CategoryId);
if (this.MinPrice.HasValue)
filters.Add((p => p.Price >= this.MinPrice);
if (this.MaxPrice.HasValue)
filters.Add(p => p.Price <= this.MaxPrice);
return filters;
}
}
}
因此,您可以在代码中使用它,如下所示:
public async Task<AbstractPagedList<Product>>GetProductsWithQuery(ProductQuery qp)
{
var products = DorianContext.Products
.Include(p => p.Category)
.Include(p => p.PriceOffers)
.AsQueryable();
foreach(var filter in qp.Filters)
{
products = products.Where(filter);
}
return await PagedList<Product>.CreateAsync(products, qp.PageNumber, qp.PageSize);
}
关于c# - foreach 中的动态过滤(ASP.NET 和 EF),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50333499/