2

不确定这是否完全可能;我发现了一些关于表达式和谓词构建器的东西,但到目前为止,还没有任何东西可以让您在事先不知道它们的情况下运行任意查询。

基本上,我有一个来自大型 SQL 数据库的对象集合,并且我正在构建一个网页 (ASP.NET MVC 4) 以允许用户显示和过滤这些对象。用户将输入的查询的复杂性会有所不同。让他们输入这些查询的最简单和最简洁的方法类似于 Visual Studio TFS 插件让您搜索工作项的方式:一个条件表,您可以在其中不断添加行。您为连接条件选择“和”或“或”,然后选择一个字段,输入一个值,然后选择是否需要匹配或不匹配的内容:

1. show items where [Field] [is|is not] [value]
2.         [and|or] [Field] [is|is not] [value]
3.         [and|or] [Field] [is|is not] [value]
etc...

什么是最简单的方法可以把它变成 LINQ-ish 的东西,我可以把它贴.ToList()在最后?到目前为止,我提出的唯一解决方案涉及一个相当大且丑陋的 switch 块,其中包含匹配各个字段并附加 a 的案例.Where(),但是为了允许用户选择“或”作为条件,我最终会做像这样的东西:

  • 虽然条件是 AND:
    • 使用大开关匹配字段
    • query = query.Where(ThisField == value);
  • 当您遇到 OR 条件时:
    • 将当前结果附加到临时列表
    • 来自完整未过滤列表的新查询
    • 使用大开关匹配字段
    • query = fullList.Where(ThisField == value);
    • 像以前一样继续
  • 当您用完条件时,将当前结果集附加到您一直使用的临时列表中,然后返回该列表。

这似乎没有我想要的那么优雅。

4

4 回答 4

8

你可以这样做:

class Program
{
    public enum Operator
    {
        And,
        Or
    }

    public class Condition
    {
        public Operator Operator { get; set; }
        public string FieldName { get; set; }
        public object Value { get; set; }
    }

    public class DatabaseRow
    {
        public int A { get; set; }
        public string B { get; set; }
    }

    static void Main(string[] args)
    {
        var conditions = new List<Condition>
        {
            new Condition { Operator = Operator.And, FieldName = "A", Value = 1 },
            new Condition { Operator = Operator.And, FieldName = "B", Value = "Asger" },
            new Condition { Operator = Operator.Or, FieldName = "A", Value = 2 },
        };

        var parameter = Expression.Parameter(typeof (DatabaseRow), "x");
        var currentExpr = MakeExpression(conditions.First(), parameter);
        foreach (var condition in conditions.Skip(1))
        {
            var nextExpr = MakeExpression(condition, parameter);
            switch (condition.Operator)
            {
                case Operator.And:
                    currentExpr = Expression.And(currentExpr, nextExpr);
                    break;
                case Operator.Or:
                    currentExpr = Expression.Or(currentExpr, nextExpr);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        var predicate = Expression.Lambda<Func<DatabaseRow, bool>>(currentExpr, parameter).Compile();

        var input = new[]
        {
            new DatabaseRow {A = 1, B = "Asger"},
            new DatabaseRow {A = 2, B = "Hans"},
            new DatabaseRow {A = 3, B = "Grethe"}
        };

        var results = input.Where(predicate).ToList();
    }

    static BinaryExpression MakeExpression(Condition condition, ParameterExpression parameter)
    {
        return Expression.Equal(
            Expression.MakeMemberAccess(parameter, typeof (DatabaseRow).GetMember(condition.FieldName)[0]),
            Expression.Constant(condition.Value));
    }
}

这假设您有一个类作为具有正确类型的数据库行的模型。然后,您可以通过正则表达式将您的条件解析为上面显示的类型条件列表,并且提供的代码可以将其转换为表达式树。生成的表达式可以编译并运行(如图所示)或转换为 SQL(只是将谓词填充到 IQueryable.Where 中)。

于 2013-02-13T12:48:10.147 回答
2

您可以使用PredicateBuilderLINQKit来执行此操作。使用它的And()Or()扩展方法,您可以为您的查询构建一个表达式树。然后你可以使用那个表达式树作为你的条件Where()。您还需要调用AsExpandable()或调用您的query, 或调用Expand()创建的表达式。

于 2013-02-13T11:28:57.950 回答
0

您可以使用 Dynamic Linq 动态添加条件。

请参阅http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

于 2013-02-12T15:27:31.027 回答
0

我已修改@asgerhallas 答案以使用用 . 并根据需要转换为适当的类型

    static BinaryExpression MakeExpression(Condition condition, ParameterExpression parameter)
    {
        var memberPath = condition.FieldName.Split(".");

        var left = Expression.Property(parameter, memberPath[0]);

        foreach (var mp in memberPath.Skip(1))
        {
            left = Expression.Property(left, ((PropertyInfo)left.Member).PropertyType.GetProperty(mp));
            
        }

        var rightType = ((PropertyInfo)left.Member).PropertyType;
        var converter = TypeDescriptor.GetConverter(rightType);

        if (!converter.CanConvertFrom(typeof(string))) throw new NotSupportedException();

        var rightValue = converter.ConvertFrom(condition.Value);

        return Expression.Equal(
            left,
            Expression.Constant(rightValue));
    }

然后您可以使用“SomeProperty.SomeThing”和“anystring”,它会根据子内容创建一个表达式,如果可以的话,将字符串转换为适当的值类型。

于 2021-11-17T17:34:01.833 回答