2012-04-24 100 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(")"); 
    } 

更好的實現是值得歡迎的,但至少現在我是暢通的。