2013-02-11 3 views
0

Я стараюсь изо всех сил попытаться создать модельный преобразователь, который проходит через любые выражения прямо к SQL через Linq2SQL, до сих пор мне удалось переназначить все свойства в дереве выражений обратно к оригиналу модель. Проблема, которую я получаю, заключается в том, что всякий раз, когда я пытаюсь использовать дерево выражений с Linq2SQL, он терпит неудачу с этой ошибкой:Модификация модели и 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 

} 

Любая помощь в определении того, что я мог бы делать неправильно была бы оценена.

ответ

0

Наконец-то выяснилось, что случилось; но я не ожидал этого. После использования процесса исключения по запросу, который может быть передан через «как есть», я удалил все методы посещения. Это сработало, однако, выражение было точно то же самое со всеми моими методами посещения, но при добавлении обратно запрос не работал.

Наконец-то выяснилось, что он перестает работать после того, как я добавил метод VisitParameter. По прошествии некоторого времени я понял, что создание нового экземпляра ParameterExpression из Expression.Parameter, даже с тем же именем, было проблемой - если он появлялся несколько раз в связанном блоке выражений (или в более конкретной области). Даже если он имел то же имя, он технически представлял собой другой параметр в целом при создании экземпляра.

Например:

param => param + 1 

Если вы используете Expression.Parameter (узел, "пары") дважды здесь, каждый экземпляр различные переменные полностью. Это по дизайну? Разумеется, параметр с таким же типом и именем в дереве выражений (в пределах той же области) должен быть одной и той же переменной?

Единственный способ, которым я столкнулся с этой проблемой, заключался в создании словаря с строкой string и ParameterExpression, поэтому, если бы снова появился тот же самый параметр имени, я бы использовал ранее созданный экземпляр параметра.

Теперь я просто должен решить, как сохранить параметры в этом словаре применимы только к своим областям - хорошо, мне, наверное, скучно, если это было слишком легко.

Смежные вопросы