模型重映射和Linq2SQL

时间:2013-02-11 02:56:25

标签: linq linq-to-sql model lambda

我正在尽力尝试创建一个模型映射器,它通过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

}

任何帮助确定我可能做错的事情都会非常感激。

1 个答案:

答案 0 :(得分:0)

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

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

例如:

param => param + 1

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

解决这个问题的唯一方法是使用types string和ParameterExpression创建一个字典,所以如果再次出现相同的命名参数,我会使用之前创建的参数实例。

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