2012-04-24 83 views
4

我正在写一个需要来源数据的工具。这个源将是用户指定的,并且可能是诸如SQL后端,专有数据库,平面文件系统之类的东西,谁知道。LINQ到自定义查询语言?

我想让我的接口获取这些类型的查询,以使用Linq,因为它似乎是最C#友好的,并且可以利用很多.NET代码。

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

我想弄清楚为用户提供简单接口的最佳方式,以指定如何将表达式树变成自定义“代码”(即“SQL”),并且它看起来相当繁琐和复杂 - - 我想象,因为它就是这样。

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

最近我可以告诉大家的是,我使用“语境”类做我自定义的分析逻辑,但我使用API​​似乎相当低的水平 - 是否有任何更高级别的操作,我可以做简单的地图操作字符串?

+0

这绝对是复杂而艰难的。据我所知,这是目前所需要的。你有代码段来显示你到目前为止?或者你陷入困境的地方? – 2012-04-24 19:14:30

+0

我错过了一些东西。好像你在问别人写这个提供者。您正在提供表达式树,并希望其他人将其转换为SQL。我倒退了吗? – Marc 2012-04-24 19:19:05

+0

@justin我会看看是否可以将一些代码拼凑在一起:它与我发送的教程链接上的内容没有什么不同。我在这方面的正确方法更加迷失。其中提出了以下问题: – 2012-04-25 04:42:52

回答

4

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

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

它实现了LINQ和你的DSL之间转换访问者模式。

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

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

更好的实现是值得欢迎的,但至少现在我是畅通的。