如何为EF表达式加入多个条件[英] How to join multiple conditions for EF Expressions

本文是小编为大家收集整理的关于如何为EF表达式加入多个条件的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

Expression<Func<Dealer, bool>> GetFilter()
        {
            Expression<Func<Dealer, bool>> f = (d) => 1 == 1;


            var s = "";

            if (QS["Province"] != null)
            {
                s = QS["Province"];
                f = d => d.Address.Province.Equals(s);

            }

            if (QS["City"] != null)
            {
                s = QS["City"];
                f = d => d.Address.City.Equals(s);                
            }

            if (QS["NameStartWith"] != null)
            {
                s = QS["NameStartWith"];
                f = d => d.DealerName.Substring(0, 1).ToUpper().Equals(s);
            }


            return f;
        }

根据查询字符串,我想生成表达式,以便使用通用存储库从数据库中获取所有经销商.

这里是代码

Expression<Func<Dealer, bool>> filter = GetFilter();            

        GenericRepository<Dealer> DealerRepository = new GenericRepository<Dealer>();
        List<Dealer> Dealers = DealerRepository.Get(filter).ToList();

推荐答案

我假设,你使用 这个存储库模式?

问题是,您只有一个表达式变量 (f),并且您正在覆盖它的内容.因此查询字符串

<块引用>

Province=ONCA&City=多伦多

会执行类似的东西

s = QS["Province"];
f = d => d.Address.Province.Equals(s);
s = QS["City"];
f = d => d.Address.City.Equals(s);   

但 f 只包含最后一个赋值.要以 EF 友好的方式组合 Lamda-Expressions,您需要一些来自 这篇 MSDN 帖子:

public class ParameterRebinder : ExpressionVisitor
{
    private readonly Dictionary<ParameterExpression, ParameterExpression> _map;

    public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
    {
        _map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
    }
    public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
    {
        return new ParameterRebinder(map).Visit(exp);
    }
    protected override Expression VisitParameter(ParameterExpression p)
    {
        ParameterExpression replacement;
        if (_map.TryGetValue(p, out replacement))
        {
            p = replacement;
        }
        return base.VisitParameter(p);
    }
}

public static class Utility
{
    public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
    {
        // build parameter map (from parameters of second to parameters of first)
        var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);
        // replace parameters in the second lambda expression with parameters from the first
        var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
        // apply composition of lambda expression bodies to parameters from the first expression 
        return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
    }
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose(second, Expression.And);
    }
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose(second, Expression.Or);
    }
}

现在您可以编写如下代码:

Expression<Func<string, bool>> filter = x => true;

filter = filter.And(x => x.Contains("a"));
filter = filter.And(x => x.Contains("b"));
filter = filter.Or(x => x.Contains("c"));

var compiled = filter.Compile();

Console.WriteLine(compiled.Invoke("aaa")); // False
Console.WriteLine(compiled.Invoke("abba")); // True
Console.WriteLine(compiled.Invoke("aac")); // True

或者在你的情况下:

Expression<Func<Dealer, bool>> GetFilter()
{
    Expression<Func<Dealer, bool>> filter = x => true;

    if (QS["Province"] != null)
    {
        var s = QS["Province"];
        filter = filter.And(d => d.Address.Province.Equals(s));
    }
    if (QS["City"] != null)
    {
        var s = QS["City"];
        filter = filter.And(d => d.Address.City.Equals(s));              
    }
    if (QS["NameStartWith"] != null)
    {
        var s = QS["NameStartWith"];
        filter = filter.And(d => d.DealerName.Substring(0, 1).ToUpper().Equals(s));
    }
    return filter;
}

本文地址:https://www.itbaoku.cn/post/1557020.html

问题描述

Expression<Func<Dealer, bool>> GetFilter()
        {
            Expression<Func<Dealer, bool>> f = (d) => 1 == 1;


            var s = "";

            if (QS["Province"] != null)
            {
                s = QS["Province"];
                f = d => d.Address.Province.Equals(s);

            }

            if (QS["City"] != null)
            {
                s = QS["City"];
                f = d => d.Address.City.Equals(s);                
            }

            if (QS["NameStartWith"] != null)
            {
                s = QS["NameStartWith"];
                f = d => d.DealerName.Substring(0, 1).ToUpper().Equals(s);
            }


            return f;
        }

Depending on querystrings I want to generate Expression for getting all the dealers from database using generic repository.

here is the code

Expression<Func<Dealer, bool>> filter = GetFilter();            

        GenericRepository<Dealer> DealerRepository = new GenericRepository<Dealer>();
        List<Dealer> Dealers = DealerRepository.Get(filter).ToList();

推荐答案

I assume, you utilize this repository pattern?

The problem is, that you have only one Expression variable (f) and you are overwriting its contents. Thus the query string

Province=ONCA&City=Toronto

Will execute something like

s = QS["Province"];
f = d => d.Address.Province.Equals(s);
s = QS["City"];
f = d => d.Address.City.Equals(s);   

But f will only contain the last assigned value. To combine Lamda-Expressions in an EF-friendly way, you need some Utility Classes from this MSDN post:

public class ParameterRebinder : ExpressionVisitor
{
    private readonly Dictionary<ParameterExpression, ParameterExpression> _map;

    public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
    {
        _map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
    }
    public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
    {
        return new ParameterRebinder(map).Visit(exp);
    }
    protected override Expression VisitParameter(ParameterExpression p)
    {
        ParameterExpression replacement;
        if (_map.TryGetValue(p, out replacement))
        {
            p = replacement;
        }
        return base.VisitParameter(p);
    }
}

public static class Utility
{
    public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
    {
        // build parameter map (from parameters of second to parameters of first)
        var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);
        // replace parameters in the second lambda expression with parameters from the first
        var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
        // apply composition of lambda expression bodies to parameters from the first expression 
        return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
    }
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose(second, Expression.And);
    }
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose(second, Expression.Or);
    }
}

Now you can write code like this:

Expression<Func<string, bool>> filter = x => true;

filter = filter.And(x => x.Contains("a"));
filter = filter.And(x => x.Contains("b"));
filter = filter.Or(x => x.Contains("c"));

var compiled = filter.Compile();

Console.WriteLine(compiled.Invoke("aaa")); // False
Console.WriteLine(compiled.Invoke("abba")); // True
Console.WriteLine(compiled.Invoke("aac")); // True

Or in your case:

Expression<Func<Dealer, bool>> GetFilter()
{
    Expression<Func<Dealer, bool>> filter = x => true;

    if (QS["Province"] != null)
    {
        var s = QS["Province"];
        filter = filter.And(d => d.Address.Province.Equals(s));
    }
    if (QS["City"] != null)
    {
        var s = QS["City"];
        filter = filter.And(d => d.Address.City.Equals(s));              
    }
    if (QS["NameStartWith"] != null)
    {
        var s = QS["NameStartWith"];
        filter = filter.And(d => d.DealerName.Substring(0, 1).ToUpper().Equals(s));
    }
    return filter;
}