4

ソースからのデータを必要とするツールを作成しています。このソースはユーザー指定であり、SQL バックエンド、独自のデータベース、フラット ファイル システムなど、誰でも知っているものである可能性があります。

Linq を使用するために、これらのタイプのクエリをフェッチするインターフェイスが必要です。Linq は、最も C# に適していると思われ、多くの .NET コードを活用できるためです。

私はいくつかの調査を行っており、素晴らしいチュートリアルhereを通じて IQueryProvider を構築することから始めました。これで大部分は理解できましたが、ユーザーに式ツリーをカスタム コードに変換させる最善の方法について混乱しています。

ユーザーが式ツリーをカスタム「コード」(つまり「SQL」) に変換する方法を指定するための簡単なインターフェイスを提供する最善の方法を見つけようとしていますが、かなり面倒で複雑に思えます。まさにそれです。

私の質問は、式ツリーをカスタム言語に変換するための最良の方法は何ですか?

私が知ることができる最も近いのは、「Context」クラスを使用してカスタム解析ロジックを実行することですが、使用する API はかなり低レベルのようです。単純に操作を文字列にマップするために実行できるより高いレベルの操作はありますか?

4

3 に答える 3

4

式ツリーをカスタムクエリ言語に変換する簡単で簡単な方法はありません。LinqExtenderを試してみることをお勧めします

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

これは、linqとDSLの間で変換するためのビジターパターンを実装します。

LinqExtenderは、カスタムLINQプロバイダーを構築するためのツールキットです。これは、元のIQyeryableおよびIQueryProvider実装の上に抽象化レイヤーを提供し、単純化された構文ツリーを提供します。さらに、内部での射影、メソッド呼び出し、順序付け、メンバー解析などもカバーしています。したがって、開発者は複雑さを除いて彼の主なタスクにもっと集中することができます

于 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 に答える