0

私は、Linq2SQLを介して式を直接SQLに戻すモデルマッパーを作成するために最善を尽くしています。これまでのところ、式ツリーのすべてのプロパティを元のモデルに再マップすることができました。私が得ている問題は、Linq2SQLで式ツリーを使用しようとすると、次のエラーで失敗することです。

「パラメーター'modelParamName'は、指定されたLINQtoEntitiesクエリ式にバインドされていませんでした。」

私のコードは以下のとおりです(式re-mapperの場合-その中にはかなりの数の拡張関数があります):

// Converting all members from using TModel to TElement
internal class ExpressionModifier<TModel, TElement> : ExpressionVisitor {

    #region Members

    #region Constructors

    internal ExpressionModifier(IQueryable source) { this.source = source; }

    #endregion

    #region Variables

    private IQueryable source;

    #endregion

    #region Methods

    internal Expression Modify(Expression expression) { var result = this.Visit(expression); return result; }

    protected override Expression VisitParameter(ParameterExpression node) {
        if(node.Type == typeof(TModel)) { return Expression.Parameter(typeof(TElement), node.Name); }
        return base.VisitParameter(node);
    }

    protected override Expression VisitConstant(ConstantExpression node) {
        if(node.Value is IQueryable) { return Expression.Constant(this.source); }
        return base.VisitConstant(node);
    }

    protected override Expression VisitMethodCall(MethodCallExpression node) {
        var arguments = node.Arguments.Select(arg => this.Visit(arg));
        var genericTypes = node.Method.GetGenericArguments().Select(arg=>this.VisitParameter(Expression.Parameter(arg)).Type);
        var newMethod = typeof(Queryable).GetMethods().SingleOrDefault(method => method.MetadataToken == node.Method.MetadataToken);
        return Expression.Call(newMethod.MakeGenericMethod(genericTypes.ToArray()), arguments);         
    }

    protected override Expression VisitLambda<T>(Expression<T> node) {
        var body = this.Visit(node.Body);
        var parameters = node.Parameters.Select(parameter => this.VisitParameter(parameter) as ParameterExpression);
        return Expression.Lambda(body, parameters.ToArray());
    }

    protected override Expression VisitMember(MemberExpression node) {
        var memberName = node.Member.Name;
        var modelAttribute = node.Member.GetAllAttributes().OfType<ModelAttribute>().SingleOrDefault();
        if(modelAttribute != null && modelAttribute.Name.IsNotNull()) { memberName = modelAttribute.Name; }
        var newMember = typeof(TElement).GetAllProperties().SingleOrDefault(property => property.Name == memberName);
        if(newMember != null) { return Expression.MakeMemberAccess(Visit(node.Expression), newMember); }
        return base.VisitMember(node);
    }

    #endregion

    #endregion

}

私が間違っている可能性があることを特定する助けをいただければ幸いです。

4

1 に答える 1

0

最後に何が悪かったのかを理解しました。しかし、それは私が期待したものではありませんでした。'as is'を通過する可能性のあるクエリで削除プロセスを使用した後、すべてのvisitメソッドを削除しました。これは機能しましたが、式はすべての訪問メソッドとまったく同じでしたが、追加して戻すとクエリは機能しませんでした。

VisitParameterメソッドを追加し直した後、最終的に機能しなくなったことがわかりました。しばらくして、Expression.Parameterから新しいParameterExpressionインスタンスを作成することが問題であることに気付きました。これは、関連する式ブロック(またはより具体的にはスコープ)内で複数回発生した場合です。同じ名前であっても、インスタンス化されたときに、技術的にはまったく異なるパラメータを表していました。

例えば:

param => param + 1

ここでExpression.Parameter(node、 "param")を2回使用すると、各インスタンスは完全に異なる変数になります。これは仕様によるものですか?確かに、(同じスコープ内の)式ツリー内の同じタイプと名前のパラメーターは同じ変数でなければなりませんか?

この問題を回避する唯一の方法は、string型とParameterExpression型の辞書を作成することでした。そのため、同じ名前のパラメーターが再び表示された場合は、以前に作成したパラメーターのインスタンスを使用します。

私は今、この辞書のパラメーターを自分のスコープにのみ適用できるようにする方法を考え出す必要があります-ああ、それが簡単すぎるとおそらく退屈するでしょう。

于 2013-02-12T22:16:39.090 に答える