LINQ到自定义查询语言?

4
我正在编写一个工具,需要从源获取数据。这个源是由用户指定的,可能是诸如SQL后端、专有数据库、平面文件系统等等。
我希望我的查询接口使用Linq,因为它似乎最符合C#语言的特性,并且可以利用大量的.NET代码。
我已经做了一些研究,开始通过这个很棒的教程here构建了一个IQueryProvider。这使我走了很长一段路,但现在我对于让用户将表达式树转换为他们自定义代码的最佳方式感到困惑。
我正在努力找出为用户提供一个简单界面的最佳方式来指定如何将表达式树转换为自定义“代码”(例如,“SQL”),但看起来相当麻烦和复杂 - 我想这正是因为确实如此。
我的问题是,将表达式树转换为自定义语言的最佳方法是什么?
据我所知,我要使用“Context”类来执行自定义解析逻辑,但我使用的API似乎非常低级 - 是否有更高级别的操作来简单地将操作映射到字符串?

这绝对是复杂和困难的。就我所知,目前需要这样做。你有代码片段展示你目前的进展吗?或者你卡在哪了? - justin.m.chase
我好像漏掉了什么。你似乎在要求别人编写提供程序。你提供了表达式树,希望别人将其转换为SQL。难道我搞错了吗? - Marc
@justin 我会看看能否整理出一些代码:它与我发送的教程链接上的内容并没有太大区别。我更迷茫于正确的处理方法。这也涉及到了以下问题: - Eddie Parker
@Marc:由于我还没有熟悉表达式树,我的一些术语可能不准确。但是我想利用Linq,因此我已经建立了一个表达式树——但是我希望用户能够采用这个表达式树并构建任何他们需要的特定领域语言(如SQL、TFS查询语言、自定义查询语言等)。我可能会编写一些基础翻译器,但我希望为我不知道的系统开放。免责声明:尝试这个项目可能有点疯狂;目前它只是一个粗略的原型。 - Eddie Parker
3个回答

4
没有一种简单或直接的方法将表达式树转换为您自定义的查询语言。您可以尝试使用LinqExtender工具包,它实现了访问者模式,用于在linq和您的dsl之间进行转换。 LinqExtender是用于构建自定义LINQ提供程序的工具包。它在原始IQyeryable和IQueryProvider实现上提供了一个抽象层,并提供了一个简化的语法树。此外,它还内部涵盖了投影,方法调用,排序,成员解析等内容。因此,开发人员可以更专注于主要任务而不必处理复杂性。请参考http://mehfuzh.github.com/LinqExtender/

1

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(")");
    }

欢迎更好的实现,但目前至少我已经解除了阻塞。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接