如何根据输入参数条件动态创建“OR”LINQ查询

mtb9vblg  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(122)

我的问题有点复杂,所以我用下面的例子简化了它。
假设我有一个简单的整数列表。我在函数中接收两个条件输入参数,它们告诉我如何过滤。类似这样:

public class input{
       public string operation = "less than";//can be <=, >=, !=, ==,<. >, etc. 
       public integer filter = 9;
}

字符串
我想根据传入的条件进行动态过滤,但我需要将其作为“OR”子句。
如何动态地创建一个linq查询,而不像这样对每个组合进行硬编码:

/*
input one = { operation = "less than" , filter = 9 }
input two = { operation = "equal to", filter =10 }
arraytoFilter { 1,2,3,4,5,6,7,8,9,10,11,12 }
*/
public int[] myFilter(input one, input two, int[] arraytoFilter){

 if (one.operation == "less than" && two.operation == "equal to"){

    return arraytoFilter.Where(x => x < one.filter || x == two.filter)

   }

    if (one.operation == "less than or equal to" && two.operation == "greater than"){

    return arraytoFilter.Where(x => x <= one.filter || x > two.filter)

   }
 /// rest of all combinations (how do I avoid doing this???)
    return arrayToFilter;
}

2exbekwf

2exbekwf1#

[更新11/20 ]
这可以在没有NuGet包的情况下通过使用System.Linq.Expressions构建表达式来完成:

using System.Linq.Expressions;

class Program
{
    public class input
    {
        public string? operation { get; set; }
        public int filter { get; set; }
    }

    static void Main()
    {
        int[] arrayToFilter = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

        input one = new()
        {
            operation = "less than",
            filter = 9
        };

        input two = new()
        {
            operation = "equal to",
            filter = 10
        };

        var filteredArray = myFilter(one, two, arrayToFilter);

        foreach (var number in filteredArray)
        {
            Console.WriteLine(number);
        }
    }

    static int[] myFilter(input one, input two, int[] arrayToFilter)
    {
        var parameter = Expression.Parameter(typeof(int), "num");
        var firstComparisonExpression = GetComparisonExpression(parameter, one.operation, one.filter);
        var secondComparisonExpression = GetComparisonExpression(parameter, two.operation, two.filter);

        // Short-circuiting OR
        var orElseExpression = Expression.OrElse(firstComparisonExpression, secondComparisonExpression);

        var lambda = Expression.Lambda<Func<int, bool>>(orElseExpression, parameter);
        
        return arrayToFilter.AsQueryable().Where(lambda.Compile()).ToArray();
    }

    static Expression GetComparisonExpression(ParameterExpression parameter, string? operation, int filter)
    {
        switch (operation)
        {
            case "less than":
                return Expression.LessThan(parameter, Expression.Constant(filter));
            case "less than or equal to":
                return Expression.LessThanOrEqual(parameter, Expression.Constant(filter));
            case "greater than":
                return Expression.GreaterThan(parameter, Expression.Constant(filter));
            case "greater than or equal to":
                return Expression.GreaterThanOrEqual(parameter, Expression.Constant(filter));
            case "equal to":
                return Expression.Equal(parameter, Expression.Constant(filter));
            case "not equal to":
                return Expression.NotEqual(parameter, Expression.Constant(filter));
            default:
                throw new ArgumentException("Invalid operation phrase.");
        }
    }
}

字符串
您可以使用System.Linq.Dynamic.Core包来编写动态LINQ查询。下面的程序已在1.3.5版本中测试:

using System.Linq.Dynamic.Core;

class Program
{
    public class input
    {
        public string? operation { get; set; }
        public int filter { get; set; }
    }

    static void Main()
    {
        int[] arrayToFilter = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

        input one = new()
        {
            operation = "less than",
            filter = 9
        };

        input two = new()
        {
            operation = "equal to",
            filter = 10
        };

        var filteredArray = myFilter(one, two, arrayToFilter);

        foreach (var number in filteredArray)
        {
            Console.WriteLine(number);
        }
    }

    static int[] myFilter(input one, input two, int[] arrayToFilter)
    {
        var firstSymbol = ConvertOperationToSymbol(one.operation);
        var secondSymbol = ConvertOperationToSymbol(two.operation);

        string dynamicQuery = $"it {firstSymbol} {one.filter} || it {secondSymbol} {two.filter}";

        return arrayToFilter.AsQueryable().Where(dynamicQuery).ToArray();
    }

    static string ConvertOperationToSymbol(string? operation)
    {
        switch (operation)
        {
            case "less than":
                return "<";
            case "less than or equal to":
                return "<=";
            case "greater than":
                return ">";
            case "greater than or equal to":
                return ">=";
            case "equal to":
                return "==";
            case "not equal to":
                return "!=";
            default:
                throw new ArgumentException("Invalid operation phrase.");
        }
    }
}

u7up0aaq

u7up0aaq2#

您应该需要强大的 * System.Linq.Expressions * 来构造表达式树和过滤器。

using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

public static int[] BuildFilter(int[] source, params Input[] inputs)
{
    var param = Expression.Parameter(typeof(int), "x");

    List<BinaryExpression> expressions = new List<BinaryExpression>();
    BinaryExpression combineExpression = null;

    if (inputs != null && inputs.Any())
    {
        foreach (Input input in inputs)
        {
            var constant = Expression.Constant(input.filter);

            switch (input.operation)
            {
                case "equal to":
                    expressions.Add(Expression.Equal(param, constant));
                    break;

                case "not equal":
                    expressions.Add(Expression.NotEqual(param, constant));
                    break;

                case "less than":
                    expressions.Add(Expression.LessThan(param, constant));
                    break;

                case "less than or equal to":
                    expressions.Add(Expression.LessThanOrEqual(param, constant));
                    break;

                case "greater than":
                    expressions.Add(Expression.GreaterThan(param, constant));
                    break;

                case "greater than or equal to":
                    expressions.Add(Expression.GreaterThanOrEqual(param, constant));
                    break;

                default:
                    throw new Exception("Operation is not supported");
            }           
        }

        combineExpression = expressions[0];

        foreach (var expr in expressions.Skip(1))
        {
            combineExpression = Expression.OrElse(combineExpression, expr);
        }
    }

    var lambda = combineExpression != null
        ? Expression.Lambda<Func<int, bool>>(combineExpression, param)
        : Expression.Lambda<Func<int, bool>>(Expression.Constant(true), param);

    return source.Where(lambda.Compile())
        .ToArray();
}

字符串
呼叫方方法:

Input[] input = new Input[]
{
    new Input { operation = "less than" , filter = 9 },
    new Input { operation = "equal to", filter = 10 }
};
int[] arraytoFilter  = new int[] { 1,2,3,4,5,6,7,8,9,10,11,12 };

var result = BuildFilter(arraytoFilter, input);

相关问题