1 //---------------------------------------------------------------------
2 // <copyright file="DefaultExpressionVisitor.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.Common.CommandTrees
13 using System.Collections.Generic;
14 using System.Data.Metadata.Edm;
15 using System.Diagnostics;
17 using CqtBuilder = System.Data.Common.CommandTrees.ExpressionBuilder.DbExpressionBuilder;
20 /// Visits each element of an expression tree from a given root expression. If any element changes, the tree is
21 /// rebuilt back to the root and the new root expression is returned; otherwise the original root expression is returned.
23 public class DefaultExpressionVisitor : DbExpressionVisitor<DbExpression>
25 private readonly Dictionary<DbVariableReferenceExpression, DbVariableReferenceExpression> varMappings = new Dictionary<DbVariableReferenceExpression, DbVariableReferenceExpression>();
27 protected DefaultExpressionVisitor()
31 protected virtual void OnExpressionReplaced(DbExpression oldExpression, DbExpression newExpression)
35 protected virtual void OnVariableRebound(DbVariableReferenceExpression fromVarRef, DbVariableReferenceExpression toVarRef)
39 protected virtual void OnEnterScope(IEnumerable<DbVariableReferenceExpression> scopeVariables)
43 protected virtual void OnExitScope()
47 protected virtual DbExpression VisitExpression(DbExpression expression)
49 DbExpression newValue = null;
50 if (expression != null)
52 newValue = expression.Accept<DbExpression>(this);
58 protected virtual IList<DbExpression> VisitExpressionList(IList<DbExpression> list)
60 return VisitList(list, this.VisitExpression);
63 protected virtual DbExpressionBinding VisitExpressionBinding(DbExpressionBinding binding)
65 DbExpressionBinding result = binding;
68 DbExpression newInput = this.VisitExpression(binding.Expression);
69 if (!object.ReferenceEquals(binding.Expression, newInput))
71 result = CqtBuilder.BindAs(newInput, binding.VariableName);
72 this.RebindVariable(binding.Variable, result.Variable);
78 protected virtual IList<DbExpressionBinding> VisitExpressionBindingList(IList<DbExpressionBinding> list)
80 return this.VisitList(list, this.VisitExpressionBinding);
83 protected virtual DbGroupExpressionBinding VisitGroupExpressionBinding(DbGroupExpressionBinding binding)
85 DbGroupExpressionBinding result = binding;
88 DbExpression newInput = this.VisitExpression(binding.Expression);
89 if (!object.ReferenceEquals(binding.Expression, newInput))
91 result = CqtBuilder.GroupBindAs(newInput, binding.VariableName, binding.GroupVariableName);
92 this.RebindVariable(binding.Variable, result.Variable);
93 this.RebindVariable(binding.GroupVariable, result.GroupVariable);
99 protected virtual DbSortClause VisitSortClause(DbSortClause clause)
101 DbSortClause result = clause;
104 DbExpression newExpression = this.VisitExpression(clause.Expression);
105 if (!object.ReferenceEquals(clause.Expression, newExpression))
107 if (!string.IsNullOrEmpty(clause.Collation))
109 result = (clause.Ascending ? CqtBuilder.ToSortClause(newExpression, clause.Collation) : CqtBuilder.ToSortClauseDescending(newExpression, clause.Collation));
113 result = (clause.Ascending ? CqtBuilder.ToSortClause(newExpression) : CqtBuilder.ToSortClauseDescending(newExpression));
120 protected virtual IList<DbSortClause> VisitSortOrder(IList<DbSortClause> sortOrder)
122 return VisitList(sortOrder, this.VisitSortClause);
125 protected virtual DbAggregate VisitAggregate(DbAggregate aggregate)
127 // Currently only function or group aggregate are possible
128 DbFunctionAggregate functionAggregate = aggregate as DbFunctionAggregate;
129 if (functionAggregate != null)
131 return VisitFunctionAggregate(functionAggregate);
134 DbGroupAggregate groupAggregate = (DbGroupAggregate)aggregate;
135 return VisitGroupAggregate(groupAggregate);
138 protected virtual DbFunctionAggregate VisitFunctionAggregate(DbFunctionAggregate aggregate)
140 DbFunctionAggregate result = aggregate;
141 if (aggregate != null)
143 EdmFunction newFunction = this.VisitFunction(aggregate.Function);
144 IList<DbExpression> newArguments = this.VisitExpressionList(aggregate.Arguments);
146 Debug.Assert(newArguments.Count == 1, "Function aggregate had more than one argument?");
148 if (!object.ReferenceEquals(aggregate.Function, newFunction) ||
149 !object.ReferenceEquals(aggregate.Arguments, newArguments))
151 if (aggregate.Distinct)
153 result = CqtBuilder.AggregateDistinct(newFunction, newArguments[0]);
157 result = CqtBuilder.Aggregate(newFunction, newArguments[0]);
164 protected virtual DbGroupAggregate VisitGroupAggregate(DbGroupAggregate aggregate)
166 DbGroupAggregate result = aggregate;
167 if (aggregate != null)
169 IList<DbExpression> newArguments = this.VisitExpressionList(aggregate.Arguments);
170 Debug.Assert(newArguments.Count == 1, "Group aggregate had more than one argument?");
172 if (!object.ReferenceEquals(aggregate.Arguments, newArguments))
174 result = CqtBuilder.GroupAggregate(newArguments[0]);
180 protected virtual DbLambda VisitLambda(DbLambda lambda)
182 EntityUtil.CheckArgumentNull(lambda, "lambda");
184 DbLambda result = lambda;
185 IList<DbVariableReferenceExpression> newFormals = this.VisitList(lambda.Variables, varRef =>
187 TypeUsage newVarType = this.VisitTypeUsage(varRef.ResultType);
188 if (!object.ReferenceEquals(varRef.ResultType, newVarType))
190 return CqtBuilder.Variable(newVarType, varRef.VariableName);
198 this.EnterScope(newFormals.ToArray()); // ToArray: Don't pass the List instance directly to OnEnterScope
199 DbExpression newBody = this.VisitExpression(lambda.Body);
202 if (!object.ReferenceEquals(lambda.Variables, newFormals) ||
203 !object.ReferenceEquals(lambda.Body, newBody))
205 result = CqtBuilder.Lambda(newBody, newFormals);
210 // Metadata 'Visitor' methods
211 protected virtual EdmType VisitType(EdmType type) { return type; }
212 protected virtual TypeUsage VisitTypeUsage(TypeUsage type) { return type; }
213 protected virtual EntitySetBase VisitEntitySet(EntitySetBase entitySet) { return entitySet; }
214 protected virtual EdmFunction VisitFunction(EdmFunction functionMetadata) { return functionMetadata; }
216 #region Private Implementation
218 private void NotifyIfChanged(DbExpression originalExpression, DbExpression newExpression)
220 if (!object.ReferenceEquals(originalExpression, newExpression))
222 this.OnExpressionReplaced(originalExpression, newExpression);
226 private IList<TElement> VisitList<TElement>(IList<TElement> list, Func<TElement, TElement> map)
228 IList<TElement> result = list;
231 List<TElement> newList = null;
232 for (int idx = 0; idx < list.Count; idx++)
234 TElement newElement = map(list[idx]);
235 if (newList == null &&
236 !object.ReferenceEquals(list[idx], newElement))
238 newList = new List<TElement>(list);
244 newList[idx] = newElement;
251 private DbExpression VisitUnary(DbUnaryExpression expression, Func<DbExpression, DbExpression> callback)
253 DbExpression result = expression;
254 DbExpression newArgument = this.VisitExpression(expression.Argument);
255 if (!object.ReferenceEquals(expression.Argument, newArgument))
257 result = callback(newArgument);
259 NotifyIfChanged(expression, result);
263 private DbExpression VisitTypeUnary(DbUnaryExpression expression, TypeUsage type, Func<DbExpression, TypeUsage, DbExpression> callback)
265 DbExpression result = expression;
267 DbExpression newArgument = this.VisitExpression(expression.Argument);
268 TypeUsage newType = this.VisitTypeUsage(type);
270 if (!object.ReferenceEquals(expression.Argument, newArgument) ||
271 !object.ReferenceEquals(type, newType))
273 result = callback(newArgument, newType);
275 NotifyIfChanged(expression, result);
279 private DbExpression VisitBinary(DbBinaryExpression expression, Func<DbExpression, DbExpression, DbExpression> callback)
281 DbExpression result = expression;
283 DbExpression newLeft = this.VisitExpression(expression.Left);
284 DbExpression newRight = this.VisitExpression(expression.Right);
285 if (!object.ReferenceEquals(expression.Left, newLeft) ||
286 !object.ReferenceEquals(expression.Right, newRight))
288 result = callback(newLeft, newRight);
290 NotifyIfChanged(expression, result);
294 private DbRelatedEntityRef VisitRelatedEntityRef(DbRelatedEntityRef entityRef)
296 RelationshipEndMember newSource;
297 RelationshipEndMember newTarget;
298 VisitRelationshipEnds(entityRef.SourceEnd, entityRef.TargetEnd, out newSource, out newTarget);
299 DbExpression newTargetRef = this.VisitExpression(entityRef.TargetEntityReference);
301 if (!object.ReferenceEquals(entityRef.SourceEnd, newSource) ||
302 !object.ReferenceEquals(entityRef.TargetEnd, newTarget) ||
303 !object.ReferenceEquals(entityRef.TargetEntityReference, newTargetRef))
305 return CqtBuilder.CreateRelatedEntityRef(newSource, newTarget, newTargetRef);
313 private void VisitRelationshipEnds(RelationshipEndMember source, RelationshipEndMember target, out RelationshipEndMember newSource, out RelationshipEndMember newTarget)
316 Debug.Assert(source.DeclaringType.EdmEquals(target.DeclaringType), "Relationship ends not declared by same relationship type?");
317 RelationshipType mappedType = (RelationshipType)this.VisitType(target.DeclaringType);
319 newSource = mappedType.RelationshipEndMembers[source.Name];
320 newTarget = mappedType.RelationshipEndMembers[target.Name];
323 private DbExpression VisitTerminal(DbExpression expression, Func<TypeUsage, DbExpression> reconstructor)
325 DbExpression result = expression;
326 TypeUsage newType = this.VisitTypeUsage(expression.ResultType);
327 if (!object.ReferenceEquals(expression.ResultType, newType))
329 result = reconstructor(newType);
331 NotifyIfChanged(expression, result);
335 private void RebindVariable(DbVariableReferenceExpression from, DbVariableReferenceExpression to)
338 // The variable is only considered rebound if the name and/or type is different.
339 // Otherwise, the original variable reference and the new variable reference are
340 // equivalent, and no rebinding of references to the old variable is necessary.
342 // When considering the new/old result types, the TypeUsage instance may be equal
343 // or equivalent, but the EdmType must be the same instance, so that expressions
344 // such as a DbPropertyExpression with the DbVariableReferenceExpression as the Instance
345 // continue to be valid.
347 if (!from.VariableName.Equals(to.VariableName, StringComparison.Ordinal) ||
348 !object.ReferenceEquals(from.ResultType.EdmType, to.ResultType.EdmType) ||
349 !from.ResultType.EdmEquals(to.ResultType))
351 this.varMappings[from] = to;
352 this.OnVariableRebound(from, to);
356 private DbExpressionBinding VisitExpressionBindingEnterScope(DbExpressionBinding binding)
358 DbExpressionBinding result = this.VisitExpressionBinding(binding);
359 this.OnEnterScope(new[] { result.Variable });
363 private void EnterScope(params DbVariableReferenceExpression[] scopeVars)
365 this.OnEnterScope(scopeVars);
368 private void ExitScope()
375 #region DbExpressionVisitor<DbExpression> Members
377 public override DbExpression Visit(DbExpression expression)
379 EntityUtil.CheckArgumentNull(expression, "expression");
381 throw EntityUtil.NotSupported(System.Data.Entity.Strings.Cqt_General_UnsupportedExpression(expression.GetType().FullName));
384 public override DbExpression Visit(DbConstantExpression expression)
386 EntityUtil.CheckArgumentNull(expression, "expression");
388 // Note that it is only safe to call DbConstantExpression.GetValue because the call to
389 // DbExpressionBuilder.Constant must clone immutable values (byte[]).
390 return VisitTerminal(expression, newType => CqtBuilder.Constant(newType, expression.GetValue()));
393 public override DbExpression Visit(DbNullExpression expression)
395 EntityUtil.CheckArgumentNull(expression, "expression");
397 return VisitTerminal(expression, CqtBuilder.Null);
400 public override DbExpression Visit(DbVariableReferenceExpression expression)
402 EntityUtil.CheckArgumentNull(expression, "expression");
404 DbExpression result = expression;
405 DbVariableReferenceExpression newRef;
406 if (this.varMappings.TryGetValue(expression, out newRef))
410 NotifyIfChanged(expression, result);
414 public override DbExpression Visit(DbParameterReferenceExpression expression)
416 EntityUtil.CheckArgumentNull(expression, "expression");
418 return VisitTerminal(expression, newType => CqtBuilder.Parameter(newType, expression.ParameterName));
421 public override DbExpression Visit(DbFunctionExpression expression)
423 EntityUtil.CheckArgumentNull(expression, "expression");
425 DbExpression result = expression;
426 IList<DbExpression> newArguments = this.VisitExpressionList(expression.Arguments);
427 EdmFunction newFunction = this.VisitFunction(expression.Function);
428 if (!object.ReferenceEquals(expression.Arguments, newArguments) ||
429 !object.ReferenceEquals(expression.Function, newFunction))
431 result = CqtBuilder.Invoke(newFunction, newArguments);
434 NotifyIfChanged(expression, result);
438 public override DbExpression Visit(DbLambdaExpression expression)
440 EntityUtil.CheckArgumentNull(expression, "expression");
442 DbExpression result = expression;
443 IList<DbExpression> newArguments = this.VisitExpressionList(expression.Arguments);
444 DbLambda newLambda = this.VisitLambda(expression.Lambda);
446 if (!object.ReferenceEquals(expression.Arguments, newArguments) ||
447 !object.ReferenceEquals(expression.Lambda, newLambda))
449 result = CqtBuilder.Invoke(newLambda, newArguments);
451 NotifyIfChanged(expression, result);
455 public override DbExpression Visit(DbPropertyExpression expression)
457 EntityUtil.CheckArgumentNull(expression, "expression");
459 DbExpression result = expression;
460 DbExpression newInstance = this.VisitExpression(expression.Instance);
461 if (!object.ReferenceEquals(expression.Instance, newInstance))
463 result = CqtBuilder.Property(newInstance, expression.Property.Name);
465 NotifyIfChanged(expression, result);
469 public override DbExpression Visit(DbComparisonExpression expression)
471 EntityUtil.CheckArgumentNull(expression, "expression");
473 switch(expression.ExpressionKind)
475 case DbExpressionKind.Equals:
476 return this.VisitBinary(expression, CqtBuilder.Equal);
478 case DbExpressionKind.NotEquals:
479 return this.VisitBinary(expression, CqtBuilder.NotEqual);
481 case DbExpressionKind.GreaterThan:
482 return this.VisitBinary(expression, CqtBuilder.GreaterThan);
484 case DbExpressionKind.GreaterThanOrEquals:
485 return this.VisitBinary(expression, CqtBuilder.GreaterThanOrEqual);
487 case DbExpressionKind.LessThan:
488 return this.VisitBinary(expression, CqtBuilder.LessThan);
490 case DbExpressionKind.LessThanOrEquals:
491 return this.VisitBinary(expression, CqtBuilder.LessThanOrEqual);
494 throw EntityUtil.NotSupported();
498 public override DbExpression Visit(DbLikeExpression expression)
500 EntityUtil.CheckArgumentNull(expression, "expression");
502 DbExpression result = expression;
504 DbExpression newArgument = this.VisitExpression(expression.Argument);
505 DbExpression newPattern = this.VisitExpression(expression.Pattern);
506 DbExpression newEscape = this.VisitExpression(expression.Escape);
508 if (!object.ReferenceEquals(expression.Argument, newArgument) ||
509 !object.ReferenceEquals(expression.Pattern, newPattern) ||
510 !object.ReferenceEquals(expression.Escape, newEscape))
512 result = CqtBuilder.Like(newArgument, newPattern, newEscape);
514 NotifyIfChanged(expression, result);
518 public override DbExpression Visit(DbLimitExpression expression)
520 EntityUtil.CheckArgumentNull(expression, "expression");
522 DbExpression result = expression;
524 DbExpression newArgument = this.VisitExpression(expression.Argument);
525 DbExpression newLimit = this.VisitExpression(expression.Limit);
527 if (!object.ReferenceEquals(expression.Argument, newArgument) ||
528 !object.ReferenceEquals(expression.Limit, newLimit))
530 Debug.Assert(!expression.WithTies, "Limit.WithTies == true?");
531 result = CqtBuilder.Limit(newArgument, newLimit);
533 NotifyIfChanged(expression, result);
537 public override DbExpression Visit(DbIsNullExpression expression)
539 EntityUtil.CheckArgumentNull(expression, "expression");
541 return VisitUnary(expression, exp =>
543 if(TypeSemantics.IsRowType(exp.ResultType))
546 return CqtBuilder.CreateIsNullExpressionAllowingRowTypeArgument(exp);
550 return CqtBuilder.IsNull(exp);
556 public override DbExpression Visit(DbArithmeticExpression expression)
558 EntityUtil.CheckArgumentNull(expression, "expression");
560 DbExpression result = expression;
561 IList<DbExpression> newArguments = this.VisitExpressionList(expression.Arguments);
562 if (!object.ReferenceEquals(expression.Arguments, newArguments))
564 switch(expression.ExpressionKind)
566 case DbExpressionKind.Divide:
567 result = CqtBuilder.Divide(newArguments[0], newArguments[1]);
570 case DbExpressionKind.Minus:
571 result = CqtBuilder.Minus(newArguments[0], newArguments[1]);
574 case DbExpressionKind.Modulo:
575 result = CqtBuilder.Modulo(newArguments[0], newArguments[1]);
578 case DbExpressionKind.Multiply:
579 result = CqtBuilder.Multiply(newArguments[0], newArguments[1]);
582 case DbExpressionKind.Plus:
583 result = CqtBuilder.Plus(newArguments[0], newArguments[1]);
586 case DbExpressionKind.UnaryMinus:
587 result = CqtBuilder.UnaryMinus(newArguments[0]);
591 throw EntityUtil.NotSupported();
594 NotifyIfChanged(expression, result);
598 public override DbExpression Visit(DbAndExpression expression)
600 EntityUtil.CheckArgumentNull(expression, "expression");
602 return VisitBinary(expression, CqtBuilder.And);
605 public override DbExpression Visit(DbOrExpression expression)
607 EntityUtil.CheckArgumentNull(expression, "expression");
609 return VisitBinary(expression, CqtBuilder.Or);
612 public override DbExpression Visit(DbNotExpression expression)
614 EntityUtil.CheckArgumentNull(expression, "expression");
616 return VisitUnary(expression, CqtBuilder.Not);
619 public override DbExpression Visit(DbDistinctExpression expression)
621 EntityUtil.CheckArgumentNull(expression, "expression");
623 return VisitUnary(expression, CqtBuilder.Distinct);
626 public override DbExpression Visit(DbElementExpression expression)
628 EntityUtil.CheckArgumentNull(expression, "expression");
630 Func<DbExpression, DbExpression> resultConstructor;
631 if (expression.IsSinglePropertyUnwrapped)
634 resultConstructor = CqtBuilder.CreateElementExpressionUnwrapSingleProperty;
638 resultConstructor = CqtBuilder.Element;
641 return VisitUnary(expression, resultConstructor);
644 public override DbExpression Visit(DbIsEmptyExpression expression)
646 EntityUtil.CheckArgumentNull(expression, "expression");
648 return VisitUnary(expression, CqtBuilder.IsEmpty);
651 public override DbExpression Visit(DbUnionAllExpression expression)
653 EntityUtil.CheckArgumentNull(expression, "expression");
655 return VisitBinary(expression, CqtBuilder.UnionAll);
658 public override DbExpression Visit(DbIntersectExpression expression)
660 EntityUtil.CheckArgumentNull(expression, "expression");
662 return VisitBinary(expression, CqtBuilder.Intersect);
665 public override DbExpression Visit(DbExceptExpression expression)
667 EntityUtil.CheckArgumentNull(expression, "expression");
669 return VisitBinary(expression, CqtBuilder.Except);
672 public override DbExpression Visit(DbTreatExpression expression)
674 EntityUtil.CheckArgumentNull(expression, "expression");
676 return this.VisitTypeUnary(expression, expression.ResultType, CqtBuilder.TreatAs);
679 public override DbExpression Visit(DbIsOfExpression expression)
681 EntityUtil.CheckArgumentNull(expression, "expression");
683 if (expression.ExpressionKind == DbExpressionKind.IsOfOnly)
685 return this.VisitTypeUnary(expression, expression.OfType, CqtBuilder.IsOfOnly);
689 return this.VisitTypeUnary(expression, expression.OfType, CqtBuilder.IsOf);
693 public override DbExpression Visit(DbCastExpression expression)
695 EntityUtil.CheckArgumentNull(expression, "expression");
697 return this.VisitTypeUnary(expression, expression.ResultType, CqtBuilder.CastTo);
700 public override DbExpression Visit(DbCaseExpression expression)
702 EntityUtil.CheckArgumentNull(expression, "expression");
704 DbExpression result = expression;
706 IList<DbExpression> newWhens = this.VisitExpressionList(expression.When);
707 IList<DbExpression> newThens = this.VisitExpressionList(expression.Then);
708 DbExpression newElse = this.VisitExpression(expression.Else);
710 if (!object.ReferenceEquals(expression.When, newWhens) ||
711 !object.ReferenceEquals(expression.Then, newThens) ||
712 !object.ReferenceEquals(expression.Else, newElse))
714 result = CqtBuilder.Case(newWhens, newThens, newElse);
716 NotifyIfChanged(expression, result);
720 public override DbExpression Visit(DbOfTypeExpression expression)
722 EntityUtil.CheckArgumentNull(expression, "expression");
724 if (expression.ExpressionKind == DbExpressionKind.OfTypeOnly)
726 return this.VisitTypeUnary(expression, expression.OfType, CqtBuilder.OfTypeOnly);
730 return this.VisitTypeUnary(expression, expression.OfType, CqtBuilder.OfType);
734 public override DbExpression Visit(DbNewInstanceExpression expression)
736 EntityUtil.CheckArgumentNull(expression, "expression");
738 DbExpression result = expression;
739 TypeUsage newType = this.VisitTypeUsage(expression.ResultType);
740 IList<DbExpression> newArguments = this.VisitExpressionList(expression.Arguments);
741 bool unchanged = (object.ReferenceEquals(expression.ResultType, newType) && object.ReferenceEquals(expression.Arguments, newArguments));
742 if (expression.HasRelatedEntityReferences)
744 IList<DbRelatedEntityRef> newRefs = this.VisitList(expression.RelatedEntityReferences, this.VisitRelatedEntityRef);
746 !object.ReferenceEquals(expression.RelatedEntityReferences, newRefs))
748 result = CqtBuilder.CreateNewEntityWithRelationshipsExpression((EntityType)newType.EdmType, newArguments, newRefs);
755 result = CqtBuilder.New(newType, System.Linq.Enumerable.ToArray(newArguments));
758 NotifyIfChanged(expression, result);
762 public override DbExpression Visit(DbRefExpression expression)
764 EntityUtil.CheckArgumentNull(expression, "expression");
766 DbExpression result = expression;
768 EntityType targetType = (EntityType)TypeHelpers.GetEdmType<RefType>(expression.ResultType).ElementType;
770 DbExpression newArgument = this.VisitExpression(expression.Argument);
771 EntityType newType = (EntityType)this.VisitType(targetType);
772 EntitySet newSet = (EntitySet)this.VisitEntitySet(expression.EntitySet);
773 if (!object.ReferenceEquals(expression.Argument, newArgument) ||
774 !object.ReferenceEquals(targetType, newType) ||
775 !object.ReferenceEquals(expression.EntitySet, newSet))
777 result = CqtBuilder.RefFromKey(newSet, newArgument, newType);
779 NotifyIfChanged(expression, result);
783 public override DbExpression Visit(DbRelationshipNavigationExpression expression)
785 EntityUtil.CheckArgumentNull(expression, "expression");
787 DbExpression result = expression;
789 RelationshipEndMember newFrom;
790 RelationshipEndMember newTo;
791 VisitRelationshipEnds(expression.NavigateFrom, expression.NavigateTo, out newFrom, out newTo);
792 DbExpression newNavSource = this.VisitExpression(expression.NavigationSource);
794 if (!object.ReferenceEquals(expression.NavigateFrom, newFrom) ||
795 !object.ReferenceEquals(expression.NavigateTo, newTo) ||
796 !object.ReferenceEquals(expression.NavigationSource, newNavSource))
798 result = CqtBuilder.Navigate(newNavSource, newFrom, newTo);
800 NotifyIfChanged(expression, result);
804 public override DbExpression Visit(DbDerefExpression expression)
806 EntityUtil.CheckArgumentNull(expression, "expression");
808 return this.VisitUnary(expression, CqtBuilder.Deref);
811 public override DbExpression Visit(DbRefKeyExpression expression)
813 EntityUtil.CheckArgumentNull(expression, "expression");
815 return this.VisitUnary(expression, CqtBuilder.GetRefKey);
818 public override DbExpression Visit(DbEntityRefExpression expression)
820 EntityUtil.CheckArgumentNull(expression, "expression");
822 return this.VisitUnary(expression, CqtBuilder.GetEntityRef);
825 public override DbExpression Visit(DbScanExpression expression)
827 EntityUtil.CheckArgumentNull(expression, "expression");
829 DbExpression result = expression;
831 EntitySetBase newSet = this.VisitEntitySet(expression.Target);
832 if (!object.ReferenceEquals(expression.Target, newSet))
834 result = CqtBuilder.Scan(newSet);
836 NotifyIfChanged(expression, result);
840 public override DbExpression Visit(DbFilterExpression expression)
842 EntityUtil.CheckArgumentNull(expression, "expression");
844 DbExpression result = expression;
846 DbExpressionBinding input = this.VisitExpressionBindingEnterScope(expression.Input);
847 DbExpression predicate = this.VisitExpression(expression.Predicate);
849 if (!object.ReferenceEquals(expression.Input, input) ||
850 !object.ReferenceEquals(expression.Predicate, predicate))
852 result = CqtBuilder.Filter(input, predicate);
854 NotifyIfChanged(expression, result);
858 public override DbExpression Visit(DbProjectExpression expression)
860 EntityUtil.CheckArgumentNull(expression, "expression");
862 DbExpression result = expression;
864 DbExpressionBinding input = this.VisitExpressionBindingEnterScope(expression.Input);
865 DbExpression projection = this.VisitExpression(expression.Projection);
867 if (!object.ReferenceEquals(expression.Input, input) ||
868 !object.ReferenceEquals(expression.Projection, projection))
870 result = CqtBuilder.Project(input, projection);
872 NotifyIfChanged(expression, result);
876 public override DbExpression Visit(DbCrossJoinExpression expression)
878 EntityUtil.CheckArgumentNull(expression, "expression");
880 DbExpression result = expression;
882 IList<DbExpressionBinding> newInputs = this.VisitExpressionBindingList(expression.Inputs);
883 if (!object.ReferenceEquals(expression.Inputs, newInputs))
885 result = CqtBuilder.CrossJoin(newInputs);
887 NotifyIfChanged(expression, result);
891 public override DbExpression Visit(DbJoinExpression expression)
893 EntityUtil.CheckArgumentNull(expression, "expression");
895 DbExpression result = expression;
897 DbExpressionBinding newLeft = this.VisitExpressionBinding(expression.Left);
898 DbExpressionBinding newRight = this.VisitExpressionBinding(expression.Right);
900 this.EnterScope(newLeft.Variable, newRight.Variable);
901 DbExpression newCondition = this.VisitExpression(expression.JoinCondition);
904 if (!object.ReferenceEquals(expression.Left, newLeft) ||
905 !object.ReferenceEquals(expression.Right, newRight) ||
906 !object.ReferenceEquals(expression.JoinCondition, newCondition))
908 if (DbExpressionKind.InnerJoin == expression.ExpressionKind)
910 result = CqtBuilder.InnerJoin(newLeft, newRight, newCondition);
912 else if (DbExpressionKind.LeftOuterJoin == expression.ExpressionKind)
914 result = CqtBuilder.LeftOuterJoin(newLeft, newRight, newCondition);
918 Debug.Assert(expression.ExpressionKind == DbExpressionKind.FullOuterJoin, "DbJoinExpression had ExpressionKind other than InnerJoin, LeftOuterJoin or FullOuterJoin?");
919 result = CqtBuilder.FullOuterJoin(newLeft, newRight, newCondition);
922 NotifyIfChanged(expression, result);
926 public override DbExpression Visit(DbApplyExpression expression)
928 EntityUtil.CheckArgumentNull(expression, "expression");
930 DbExpression result = expression;
932 DbExpressionBinding newInput = this.VisitExpressionBindingEnterScope(expression.Input);
933 DbExpressionBinding newApply = this.VisitExpressionBinding(expression.Apply);
936 if (!object.ReferenceEquals(expression.Input, newInput) ||
937 !object.ReferenceEquals(expression.Apply, newApply))
939 if (DbExpressionKind.CrossApply == expression.ExpressionKind)
941 result = CqtBuilder.CrossApply(newInput, newApply);
945 Debug.Assert(expression.ExpressionKind == DbExpressionKind.OuterApply, "DbApplyExpression had ExpressionKind other than CrossApply or OuterApply?");
946 result = CqtBuilder.OuterApply(newInput, newApply);
949 NotifyIfChanged(expression, result);
953 public override DbExpression Visit(DbGroupByExpression expression)
955 EntityUtil.CheckArgumentNull(expression, "expression");
957 DbExpression result = expression;
959 DbGroupExpressionBinding newInput = this.VisitGroupExpressionBinding(expression.Input);
960 this.EnterScope(newInput.Variable);
961 IList<DbExpression> newKeys = this.VisitExpressionList(expression.Keys);
963 this.EnterScope(newInput.GroupVariable);
964 IList<DbAggregate> newAggs = this.VisitList<DbAggregate>(expression.Aggregates, this.VisitAggregate);
967 if (!object.ReferenceEquals(expression.Input, newInput) ||
968 !object.ReferenceEquals(expression.Keys, newKeys) ||
969 !object.ReferenceEquals(expression.Aggregates, newAggs))
971 RowType groupOutput =
972 TypeHelpers.GetEdmType<RowType>(TypeHelpers.GetEdmType<CollectionType>(expression.ResultType).TypeUsage);
974 var boundKeys = groupOutput.Properties.Take(newKeys.Count).Select(p => p.Name).Zip(newKeys).ToList();
975 var boundAggs = groupOutput.Properties.Skip(newKeys.Count).Select(p => p.Name).Zip(newAggs).ToList();
977 result = CqtBuilder.GroupBy(newInput, boundKeys, boundAggs);
979 NotifyIfChanged(expression, result);
983 public override DbExpression Visit(DbSkipExpression expression)
985 EntityUtil.CheckArgumentNull(expression, "expression");
987 DbExpression result = expression;
989 DbExpressionBinding newInput = this.VisitExpressionBindingEnterScope(expression.Input);
990 IList<DbSortClause> newSortOrder = this.VisitSortOrder(expression.SortOrder);
992 DbExpression newCount = this.VisitExpression(expression.Count);
994 if (!object.ReferenceEquals(expression.Input, newInput) ||
995 !object.ReferenceEquals(expression.SortOrder, newSortOrder) ||
996 !object.ReferenceEquals(expression.Count, newCount))
998 result = CqtBuilder.Skip(newInput, newSortOrder, newCount);
1000 NotifyIfChanged(expression, result);
1004 public override DbExpression Visit(DbSortExpression expression)
1006 EntityUtil.CheckArgumentNull(expression, "expression");
1008 DbExpression result = expression;
1010 DbExpressionBinding newInput = this.VisitExpressionBindingEnterScope(expression.Input);
1011 IList<DbSortClause> newSortOrder = this.VisitSortOrder(expression.SortOrder);
1014 if (!object.ReferenceEquals(expression.Input, newInput) ||
1015 !object.ReferenceEquals(expression.SortOrder, newSortOrder))
1017 result = CqtBuilder.Sort(newInput, newSortOrder);
1019 NotifyIfChanged(expression, result);
1023 public override DbExpression Visit(DbQuantifierExpression expression)
1025 EntityUtil.CheckArgumentNull(expression, "expression");
1027 DbExpression result = expression;
1029 DbExpressionBinding input = this.VisitExpressionBindingEnterScope(expression.Input);
1030 DbExpression predicate = this.VisitExpression(expression.Predicate);
1033 if (!object.ReferenceEquals(expression.Input, input) ||
1034 !object.ReferenceEquals(expression.Predicate, predicate))
1036 if (DbExpressionKind.All == expression.ExpressionKind)
1038 result = CqtBuilder.All(input, predicate);
1042 Debug.Assert(expression.ExpressionKind == DbExpressionKind.Any, "DbQuantifierExpression had ExpressionKind other than All or Any?");
1043 result = CqtBuilder.Any(input, predicate);
1046 NotifyIfChanged(expression, result);