Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/entity-framework/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# foreach中的动态筛选(ASP.NET和EF)_C#_Entity Framework - Fatal编程技术网

C# foreach中的动态筛选(ASP.NET和EF)

C# foreach中的动态筛选(ASP.NET和EF),c#,entity-framework,C#,Entity Framework,我有一个非常简单的案例,有一个控制器和一个存储库 控制器: [HttpGet] public async Task<IActionResult> GetProductList(ProductQuery queryparams) { var products = await uow.ProductRepo.GetProductsWithQuery(queryparams); var productsToReturn = mapp

我有一个非常简单的案例,有一个控制器和一个存储库

控制器:

    [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);
    }
我们如何构造一个动态/泛型逻辑来对CategoryId、MinPrice和MaxPrice进行过滤,而不是枯燥的注释部分。 例如,在ProductQuery属性列表的foreach块中

也许我们可以像下面这样使用dictionary对象和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);
        }

我不想使用反射。对于此类案例的最佳实践的想法或评论也非常感谢。

也许您可以使用测试函数和表达式对的值元组:

ProductQuery filter = ... // initialize here

var exprs = new List<(Func<ProductQuery, object>, Expression<Func<Product, bool>>)>() {
    (f => f.CategoryId, p => p.CategoryId == filter.CategoryId),
    (f => f.MinPrice, p => p.Price >= filter.MinPrice),
    (f => f.MaxPrice, p => p.Price <= filter.MaxPrice)
};

foreach (var (test, expr) in exprs) {
    if (test(filter) != null) {
        products = products.Where(expr);
    }
}
这样,就可以避免定义空检查测试

解析表达式的代码应该更加灵活-如果表达式中的filter属性是第一个呢?如果某个地方涉及到转换怎么办

我还建议将构建单个筛选器表达式的逻辑封装为ProductQuery的属性:

您可以自己实现,但我强烈建议使用:

我所做的是有效的,我可以继续这样做,但这将 结果大量的重复逻辑。因为这是一个玩具项目,我 我正在寻找改进的方法。这样一个项目,这是 我同意你的看法

因此,避免打破干燥原则的最佳方法是在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);
}

将为filter对象生成表达式的逻辑封装为filter上的方法是否有意义?您实际所做的很好。你说无聊是什么意思?@ZevSpitz,是的,这很有意义,但首先我需要找到一个解决方案,解决将ProductQuery属性名的字符串表示形式与cases@CodeNotFound我所做的工作是的,我可以继续这样做,但这将导致大量的重复逻辑。因为这是一个玩具项目,我正在寻找改进的方法。对于这样一个项目,我同意,这太过分了。ProductQuery AFAIK属性名称的字符串表示,唯一的方法是通过反射。即使您将对象转换为JObject,我也非常确定Newtonsoft JSON正在使用引擎盖下的反射。我如何使用FilterType?@ElHuracan它只是一个占位符;我没有注意到您问题中的ProductQuery类型。更新。在元组示例中,筛选器是如何定义的?@AussieJoe我已经修复了它。筛选器是ProductQuery的一个实例。局部列表变量不需要是表达式吗?确实修复了。这必须是避免打破干燥原则的最佳方法,对于哪个值n?:@ZevSpitz我添加它是因为OP的评论。我不在他的项目中工作。他说他希望避免重复,这样问题中的样本就不能代表他正在进行的真实项目。也许他对复制品的看法是对的,所以我的回答是为了帮助他不要破坏普林西比。您可以联系他,了解有关其项目的更多详细信息:
ProductQuery filter = ... // initialize here

var exprs = new List<Expression<Func<Product, bool>>>() {
    p => p.CategoryId == filter.CategoryId,
    p => p.Price >= filter.MinPrice,
    p => p.Price <= filter.MaxPrice
};

foreach (var expr in exprs) {
    var pi = ((expr.Body as BinaryExpression)
        .Right as MemberExpression)
        .Member as PropertyInfo;
    if (pi.GetValue(filter) != null) {
        products = products.Where(expr);
    }
}
public Expression<Product, bool> Filter => {
    get {
        // implementation at end of answer
    }
}
products = products.Where(filter.Filter);
public Expression<Func<Product, bool>> Filter {
    get {
        var exprs = new List<Expression<Func<Product, bool>>>() {
            p => p.CategoryId == this.CategoryId,
            p => p.Price >= this.MinPrice,
            p => p.Price <= this.MaxPrice
        }.Where(expr => {
            PropertyInfo mi = ((expr.Body as BinaryExpression)
                .Right as MemberExpression)
                .Member as PropertyInfo;
            return mi.GetValue(this) != null;
        });

        var predicate = PredicateBuilder.True<Product>();
        foreach (var expr in exprs) {
            predicate = predicate.And(expr);
        }

        return predicate;
    }
}
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);
}