4

我正在编写一个需要来自源的数据的工具。此源将由用户指定,可能是 SQL 后端、专有数据库、平面文件系统等,谁知道呢。

我希望我的界面能够获取这些类型的查询,以使用 Linq,因为它似乎对 C# 最友好,并且可以利用大量 .NET 代码。

我一直在做一些研究,并开始通过这里的精彩教程构建一个 IQueryProvider 。它让我获得了很大的帮助,但现在我对让用户将表达式树转换为他们的自定义代码的最佳方式感到困惑。

我试图找出最好的方法来为用户提供一个简单的界面来指定如何将表达式树转换为自定义“代码”(即“SQL”),这似乎相当麻烦和复杂——我想是因为正是这样。

我的问题是,将表达式树转换为自定义语言的最佳方法是什么?

我能说的最接近的是我将使用“上下文”类来执行我的自定义解析逻辑,但我使用的 API 似乎相当低级 - 是否有任何更高级别的操作可以简单地将操作映射到字符串?

4

3 回答 3

4

将表达式树转换为您的自定义查询语言没有简单或直接的方法。您可能想尝试一下 LinqExtender

http://mehfuzh.github.com/LinqExtender/

它实现了一种访问者模式,用于在 linq 和您的 dsl 之间进行转换。

LinqExtender 是一个用于构建自定义 LINQ 提供程序的工具包。它在原始 IQyeryable 和 IQueryProvider 实现之上提供了一个抽象层,并提供了一个简化的语法树。此外,它在内部涵盖了诸如投影、方法调用、ordery by、成员解析等内容。因此开发人员可以更专注于他的主要任务减去复杂性

.

于 2012-04-24T19:13:49.767 回答
1

一般来说,将树结构转换为其他形式的最佳方法是使用访问者模式

具体查看 msdn 上的ExpressionVisitor类。

于 2012-04-24T19:17:35.847 回答
0

所以,我确实调查了访问者模式,但我无法让它以我喜欢的方式工作,所以我有点破解了一个解决方案。:/

我已经使用基本示例创建了一个基本 QueryContext 来解析树并构建字符串集合。我最终得到的是这样的东西。它绝不是完整的,但它是一个不错的开始:

    public object Execute(Expression expression, bool IsEnumerable)
    {
        // Find the call to Where() and get the lambda expression predicate.
        InnermostWhereFinder whereFinder = new InnermostWhereFinder();
        MethodCallExpression whereExpression = whereFinder.GetInnermostWhere(expression);
        LambdaExpression lambdaExpression = (LambdaExpression)((UnaryExpression)(whereExpression.Arguments[1])).Operand;

        // Send the lambda expression through the partial evaluator.
        lambdaExpression = (LambdaExpression)Evaluator.PartialEval(lambdaExpression);

        // Assemble the strings necessary to build this.
        var strings = new List<string>();

        GetStrings(lambdaExpression.Body, strings);

        var query = String.Join(" ", strings);

        return ExecuteQuery(query);
    }

    public abstract object ExecuteQuery(string whereClause);

    public abstract Dictionary<ExpressionType, string> ExpressionTypeToStringMap { get; }

    public abstract string FormatFieldName(string fieldName);
    public abstract string FormatConstant(string constant);

    void GetStrings(System.Linq.Expressions.Expression expression, List<string> toReturn)
    {
        if (expression is BinaryExpression)
        {
            // Binary expression.  Recurse and add to the list.
            GetStrings((BinaryExpression)(expression), toReturn);
        }
        else if (expression is MemberExpression)
        {
            var e = (MemberExpression)(expression);
            toReturn.Add(FormatFieldName(e.Member.Name));
        }
        else if (expression is ConstantExpression)
        {
            var e = (ConstantExpression)(expression);
            toReturn.Add(FormatConstant((string)(e.Value)));
        }
        else
        {
            throw new NotImplementedException("Unaware of how to handle type " + expression.GetType().ToString());
        }
    }

    string NodeTypeToString(ExpressionType type)
    {
        var map = ExpressionTypeToStringMap;

        if(map.ContainsKey(type))
        {
            return map[type];
        }

        throw new NotImplementedException("Type '" + type.ToString() + "' not implemented in ExpressionTypeToStringMap.");
    }

    void GetStrings(BinaryExpression expression, List<string> toReturn)
    {
        toReturn.Add("(");

        if (expression.Left != null)
            GetStrings(expression.Left, toReturn);

        toReturn.Add(NodeTypeToString(expression.NodeType));

        if (expression.Right != null)
            GetStrings(expression.Right, toReturn);

        toReturn.Add(")");
    }

欢迎更好的实现,但至少现在我没有被阻止。

于 2012-04-26T04:58:11.160 回答