0

我正在尽最大努力尝试创建一个模型映射器,该模型映射器通过 Linq2SQL 将任何表达式直接传递回 SQL,到目前为止,我已经设法将表达式树中的所有属性重新映射回原始模型。我遇到的问题是,每当我尝试将表达式树与 Linq2SQL 一起使用时,它都会失败并出现以下错误:

“参数 'modelParamName' 未绑定在指定的 LINQ to Entities 查询表达式中。”

我的代码如下(对于表达式重新映射器 - 我确实有很多扩展函数):

// 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

终于弄清楚了哪里出了问题;但这不是我所期望的。在对可以“按原样”传递的查询使用消除过程后,我删除了所有访问方法。这行得通,但是,表达式与我所有的访问方法完全相同,但是当添加回来时,查询不起作用。

在我添加回 VisitParameter 方法后,它终于发现它停止工作了。一段时间后,我意识到从 Expression.Parameter 创建一个新的 ParameterExpression 实例,即使具有相同的名称,也是问题所在 - 如果它在相关的表达式块(或更具体地说是范围)中出现不止一次。即使它具有相同的名称,它在实例化时在技术上完全代表一个不同的参数。

例如:

param => param + 1

如果您在此处使用 Expression.Parameter(node, "param") 两次,则每个实例完全是不同的变量。这是设计使然吗?表达式树(在同一范围内)中具有相同类型和名称的参数肯定是同一个变量吗?

我解决这个问题的唯一方法是创建一个类型为 string 和 ParameterExpression 的字典,因此如果再次出现相同的命名参数,我将使用先前创建的参数实例。

我现在只需要弄清楚如何让这本字典中的参数只适用于它们自己的范围 - 嗯,如果它太容易我可能会感到无聊。

于 2013-02-12T22:16:39.090 回答