1 //---------------------------------------------------------------------
2 // <copyright file="ExpressionConverter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 //---------------------------------------------------------------------
9 namespace System.Data.Objects.ELinq
11 using System.Collections;
12 using System.Collections.Generic;
13 using System.Data.Common;
14 using System.Data.Common.CommandTrees;
15 using System.Data.Common.CommandTrees.ExpressionBuilder;
16 using System.Data.Common.EntitySql;
17 using System.Data.Common.Utils;
18 using System.Data.Entity;
19 using System.Data.Metadata.Edm;
20 using System.Diagnostics;
21 using System.Globalization;
23 using System.Linq.Expressions;
24 using System.Reflection;
28 /// Class supporting conversion of LINQ expressions to EDM CQT expressions.
30 internal sealed partial class ExpressionConverter
33 private readonly Funcletizer _funcletizer;
34 private readonly Perspective _perspective;
35 private readonly Expression _expression;
36 private readonly BindingContext _bindingContext;
37 private Func<bool> _recompileRequired;
38 private List<KeyValuePair<ObjectParameter, QueryParameterExpression>> _parameters;
39 private Dictionary<DbExpression, Span> _spanMappings;
40 private MergeOption? _mergeOption;
41 private Dictionary<Type, InitializerMetadata> _initializers;
43 private HashSet<ObjectQuery> _inlineEntitySqlQueries;
44 private int _ignoreInclude;
45 private readonly AliasGenerator _aliasGenerator = new AliasGenerator("LQ", 0);
46 private readonly OrderByLifter _orderByLifter;
49 private const string s_visualBasicAssemblyFullName =
50 "Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
51 private static readonly Dictionary<ExpressionType, Translator> s_translators = InitializeTranslators();
53 internal const string s_entityCollectionCountPropertyName = "Count";
54 internal const string s_nullableHasValuePropertyName = "HasValue";
55 internal const string s_nullableValuePropertyName = "Value";
58 /// Gets the name of the key column appearing in ELinq GroupBy projections
60 internal const string KeyColumnName = "Key";
63 /// Gets the name of the group column appearing in ELinq CQTs (used in GroupBy expressions)
65 internal const string GroupColumnName = "Group";
68 /// Gets the name of the parent column appearing in ELinq EntityCollection projections
70 internal const string EntityCollectionOwnerColumnName = "Owner";
73 /// Gets the name of the children column appearing in ELinq EntityCollection projections
75 internal const string EntityCollectionElementsColumnName = "Elements";
78 /// The Edm namespace name, used for canonical functions
80 internal const string EdmNamespaceName = "Edm";
83 #region Canonical Function Names
84 private const string Concat = "Concat";
85 private const string IndexOf = "IndexOf";
86 private const string Length = "Length";
87 private const string Right = "Right";
88 private const string Substring = "Substring";
89 private const string ToUpper = "ToUpper";
90 private const string ToLower = "ToLower";
91 private const string Trim = "Trim";
92 private const string LTrim = "LTrim";
93 private const string RTrim = "RTrim";
94 private const string Reverse = "Reverse";
95 private const string BitwiseAnd = "BitwiseAnd";
96 private const string BitwiseOr = "BitwiseOr";
97 private const string BitwiseNot = "BitwiseNot";
98 private const string BitwiseXor = "BitwiseXor";
99 private const string CurrentUtcDateTime = "CurrentUtcDateTime";
100 private const string CurrentDateTimeOffset = "CurrentDateTimeOffset";
101 private const string CurrentDateTime = "CurrentDateTime";
102 private const string Year = "Year";
103 private const string Month = "Month";
104 private const string Day = "Day";
105 private const string Hour = "Hour";
106 private const string Minute = "Minute";
107 private const string Second = "Second";
108 private const string Millisecond = "Millisecond";
111 #region Additional Entity function names
112 private const string AsUnicode = "AsUnicode";
113 private const string AsNonUnicode = "AsNonUnicode";
117 #region Constructors and static initializors
118 internal ExpressionConverter(Funcletizer funcletizer, Expression expression)
120 EntityUtil.CheckArgumentNull(funcletizer, "funcletizer");
121 EntityUtil.CheckArgumentNull(expression, "expression");
123 // Funcletize the expression (identify subexpressions that should be evaluated
125 _funcletizer = funcletizer;
126 expression = funcletizer.Funcletize(expression, out _recompileRequired);
128 // Normalize the expression (replace obfuscated parts of the tree with simpler nodes)
129 LinqExpressionNormalizer normalizer = new LinqExpressionNormalizer();
130 _expression = normalizer.Visit(expression);
132 _perspective = funcletizer.RootContext.Perspective;
133 _bindingContext = new BindingContext();
135 _orderByLifter = new OrderByLifter(_aliasGenerator);
138 // initialize translator dictionary (which support identification of translators
139 // for LINQ expression node types)
140 private static Dictionary<ExpressionType, Translator> InitializeTranslators()
142 Dictionary<ExpressionType, Translator> translators = new Dictionary<ExpressionType, Translator>();
143 foreach (Translator translator in GetTranslators())
145 foreach (ExpressionType nodeType in translator.NodeTypes)
147 translators.Add(nodeType, translator);
154 private static IEnumerable<Translator> GetTranslators()
156 yield return new AndAlsoTranslator();
157 yield return new OrElseTranslator();
158 yield return new LessThanTranslator();
159 yield return new LessThanOrEqualsTranslator();
160 yield return new GreaterThanTranslator();
161 yield return new GreaterThanOrEqualsTranslator();
162 yield return new EqualsTranslator();
163 yield return new NotEqualsTranslator();
164 yield return new ConvertTranslator();
165 yield return new ConstantTranslator();
166 yield return new NotTranslator();
167 yield return new MemberAccessTranslator();
168 yield return new ParameterTranslator();
169 yield return new MemberInitTranslator();
170 yield return new NewTranslator();
171 yield return new AddTranslator();
172 yield return new ConditionalTranslator();
173 yield return new DivideTranslator();
174 yield return new ModuloTranslator();
175 yield return new SubtractTranslator();
176 yield return new MultiplyTranslator();
177 yield return new NegateTranslator();
178 yield return new UnaryPlusTranslator();
179 yield return new MethodCallTranslator();
180 yield return new CoalesceTranslator();
181 yield return new AsTranslator();
182 yield return new IsTranslator();
183 yield return new QuoteTranslator();
184 yield return new AndTranslator();
185 yield return new OrTranslator();
186 yield return new ExclusiveOrTranslator();
187 yield return new ExtensionTranslator();
188 yield return new NewArrayInitTranslator();
189 yield return new ListInitTranslator();
190 yield return new NotSupportedTranslator(
191 ExpressionType.LeftShift,
192 ExpressionType.RightShift,
193 ExpressionType.ArrayLength,
194 ExpressionType.ArrayIndex,
195 ExpressionType.Invoke,
196 ExpressionType.Lambda,
197 ExpressionType.NewArrayBounds,
198 ExpressionType.Power);
203 private EdmItemCollection EdmItemCollection
207 return (EdmItemCollection)_funcletizer.RootContext.MetadataWorkspace.GetItemCollection(DataSpace.CSpace, true);
210 internal DbProviderManifest ProviderManifest
214 return ((StoreItemCollection)_funcletizer.RootContext.MetadataWorkspace.GetItemCollection(DataSpace.SSpace)).StoreProviderManifest;
217 internal System.Collections.ObjectModel.ReadOnlyCollection<KeyValuePair<ObjectParameter, QueryParameterExpression>> GetParameters()
219 if (null != _parameters) { return _parameters.AsReadOnly(); }
222 internal MergeOption? PropagatedMergeOption { get { return _mergeOption; } }
223 internal Span PropagatedSpan { get { return _span; } }
224 internal Func<bool> RecompileRequired { get { return _recompileRequired; } }
225 internal int IgnoreInclude { get { return _ignoreInclude; } set { _ignoreInclude = value; } }
226 internal AliasGenerator AliasGenerator { get { return _aliasGenerator; } }
229 #region Internal methods
230 // Convert the LINQ expression to a CQT expression and (optional) Span information.
231 // Span information will only be present if ObjectQuery instances that specify Spans
232 // are referenced from the LINQ expression in a manner consistent with the Span combination
233 // rules, otherwise the Span for the CQT expression will be null.
234 internal DbExpression Convert()
236 DbExpression result = this.TranslateExpression(_expression);
237 if (!TryGetSpan(result, out _span))
244 internal static bool CanFuncletizePropertyInfo(PropertyInfo propertyInfo)
246 return MemberAccessTranslator.CanFuncletizePropertyInfo(propertyInfo);
249 internal bool CanIncludeSpanInfo()
251 return (_ignoreInclude == 0);
255 #region Private Methods
257 private void NotifyMergeOption(MergeOption mergeOption)
259 if (!this._mergeOption.HasValue)
261 this._mergeOption = mergeOption;
265 // Requires: metadata must not be null.
267 // Effects: adds initializer metadata to this query context.
269 // Ensures that the given initializer metadata is valid within the current converter context.
270 // We do not allow two incompatible structures representing the same type within a query, e.g.,
272 // outer.Join(inner, o => new Foo { X = o.ID }, i => new Foo { Y = i.ID }, ...
274 // since this introduces a discrepancy between the CLR (where comparisons between Foo are aware
275 // of both X and Y) and in ELinq (where comparisons are based on the row structure only), resulting
276 // in the following join predicates:
278 // Linq: foo1 == foo2 (which presumably amounts to foo1.X == foo2.X && foo1.Y == foo2.Y
279 // ELinq: foo1.X == foo2.Y
281 // Similar problems occur with set operations such as Union and Concat, where one of the initialization
282 // patterns may be ignored.
284 // This method performs an overly strict check, requiring that all initializers for a given type
285 // are structurally equivalent.
286 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2301", Justification = "metadata.ClrType is not expected to be an Embedded Interop Type.")]
287 internal void ValidateInitializerMetadata(InitializerMetadata metadata)
289 Debug.Assert(null != metadata);
290 InitializerMetadata existingMetadata;
291 if (_initializers != null && _initializers.TryGetValue(metadata.ClrType, out existingMetadata))
293 // Verify the initializers are compatible.
294 if (!metadata.Equals(existingMetadata))
296 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedHeterogeneousInitializers(
297 ExpressionConverter.DescribeClrType(metadata.ClrType)));
302 // Register the metadata so that subsequent initializers for this type can be verified.
303 if (_initializers == null)
305 _initializers = new Dictionary<Type, InitializerMetadata>();
307 _initializers.Add(metadata.ClrType, metadata);
311 private void AddParameter(QueryParameterExpression queryParameter)
313 if (null == _parameters)
315 _parameters = new List<KeyValuePair<ObjectParameter, QueryParameterExpression>>();
317 if (!_parameters.Select(p => p.Value).Contains(queryParameter))
319 ObjectParameter parameter = new ObjectParameter(queryParameter.ParameterReference.ParameterName, queryParameter.Type);
320 _parameters.Add(new KeyValuePair<ObjectParameter, QueryParameterExpression>(parameter, queryParameter));
324 private bool IsQueryRoot(Expression Expression)
327 // An expression is the query root if it was the expression used
328 // when constructing this converter.
330 return object.ReferenceEquals(_expression, Expression);
333 #region Span Mapping maintenance methods
336 /// Adds a new mapping from DbExpression => Span information for the specified expression,
337 /// after first ensuring that the mapping dictionary has been instantiated.
339 /// <param name="expression">The expression for which Span information should be added</param>
340 /// <param name="span">
341 /// The Span information, which may be <c>null</c>.
342 /// If <c>null</c>, no attempt is made to update the dictionary of span mappings.
344 /// <returns>The original <paramref name="expression"/> argument, to allow <c>return AddSpanMapping(expression, span)</c> scenarios</returns>
345 private DbExpression AddSpanMapping(DbExpression expression, Span span)
347 if (span != null && this.CanIncludeSpanInfo())
349 if (null == _spanMappings)
351 _spanMappings = new Dictionary<DbExpression, Span>();
353 Span storedSpan = null;
354 if (_spanMappings.TryGetValue(expression, out storedSpan))
356 foreach (Span.SpanPath sp in span.SpanList)
358 storedSpan.AddSpanPath(sp);
360 _spanMappings[expression] = storedSpan;
364 _spanMappings[expression] = span;
372 /// Attempts to retrieve Span information for the specified DbExpression.
374 /// <param name="expression">The expression for which Span information should be retrieved.</param>
375 /// <param name="span">Will contain the Span information for the specified expression if it is present in the Span mapping dictionary.</param>
376 /// <returns><c>true</c> if Span information was retrieved for the specified expression and <paramref name="span"/> now contains this information; otherwise <c>false</c>.</returns>
377 private bool TryGetSpan(DbExpression expression, out Span span)
379 if (_spanMappings != null)
381 return _spanMappings.TryGetValue(expression, out span);
389 /// Removes the Span mapping entry for the specified <paramref name="from"/> expression,
390 /// and creates a new entry for the specified <paramref name="to"/> expression that maps
391 /// to the <paramref name="from"/> expression's original Span information. If no Span
392 /// information is present for the specified <paramref name="from"/> expression then no
393 /// changes are made to the Span mapping dictionary.
395 /// <param name="from">The expression from which to take Span information</param>
396 /// <param name="to">The expression to which the Span information should be applied</param>
397 private void ApplySpanMapping(DbExpression from, DbExpression to)
400 if (TryGetSpan(from, out argumentSpan))
402 AddSpanMapping(to, argumentSpan);
407 /// Unifies the Span information from the specified <paramref name="left"/> and <paramref name="right"/>
408 /// expressions, and applies it to the specified <paramref name="to"/> expression. Unification proceeds
410 /// - If neither <paramref name="left"/> nor <paramref name="right"/> have Span information, no changes are made
411 /// - If one of <paramref name="left"/> or <paramref name="right"/> has Span information, that single Span information
412 /// entry is removed from the Span mapping dictionary and used to create a new entry that maps from the <paramref name="to"/>
413 /// expression to the Span information.
414 /// - If both <paramref name="left"/> and <paramref name="right"/> have Span information, both entries are removed
415 /// from the Span mapping dictionary, a new Span is created that contains the union of the original Spans, and
416 /// a new entry is added to the dictionary that maps from <paramref name="to"/> expression to this new Span.
418 /// <param name="left">The first expression argument</param>
419 /// <param name="right">The second expression argument</param>
420 /// <param name="to">The result expression</param>
421 private void UnifySpanMappings(DbExpression left, DbExpression right, DbExpression to)
423 Span leftSpan = null;
424 Span rightSpan = null;
426 bool hasLeftSpan = TryGetSpan(left, out leftSpan);
427 bool hasRightSpan = TryGetSpan(right, out rightSpan);
428 if (!hasLeftSpan && !hasRightSpan)
433 Debug.Assert(leftSpan != null || rightSpan != null, "Span mappings contain null?");
434 AddSpanMapping(to, Span.CopyUnion(leftSpan, rightSpan));
438 // The following methods correspond to query builder methods on ObjectQuery
439 // and MUST be called by expression translators (instead of calling the equivalent
440 // CommandTree.CreateXxExpression methods) to ensure that Span information flows
441 // correctly to the root of the Command Tree as it is constructed by converting
442 // the LINQ expression tree. Each method correctly maintains a Span mapping (if required)
443 // for its resulting expression, based on the Span mappings of its argument expression(s).
445 private DbDistinctExpression Distinct(DbExpression argument)
447 DbDistinctExpression retExpr = argument.Distinct();
448 ApplySpanMapping(argument, retExpr);
452 private DbExceptExpression Except(DbExpression left, DbExpression right)
454 DbExceptExpression retExpr = left.Except(right);
455 ApplySpanMapping(left, retExpr);
459 private DbExpression Filter(DbExpressionBinding input, DbExpression predicate)
461 DbExpression retExpr = _orderByLifter.Filter(input, predicate);
462 ApplySpanMapping(input.Expression, retExpr);
466 private DbIntersectExpression Intersect(DbExpression left, DbExpression right)
468 DbIntersectExpression retExpr = left.Intersect(right);
469 UnifySpanMappings(left, right, retExpr);
473 private DbExpression Limit(DbExpression argument, DbExpression limit)
475 DbExpression retExpr = _orderByLifter.Limit(argument, limit);
476 ApplySpanMapping(argument, retExpr);
480 private DbExpression OfType(DbExpression argument, TypeUsage ofType)
482 DbExpression retExpr = _orderByLifter.OfType(argument, ofType);
483 ApplySpanMapping(argument, retExpr);
487 private DbExpression Project(DbExpressionBinding input, DbExpression projection)
489 DbExpression retExpr = _orderByLifter.Project(input, projection);
490 // For identity projection only, the Span is preserved
491 if (projection.ExpressionKind == DbExpressionKind.VariableReference &&
492 ((DbVariableReferenceExpression)projection).VariableName.Equals(input.VariableName, StringComparison.Ordinal))
494 ApplySpanMapping(input.Expression, retExpr);
499 private DbSortExpression Sort(DbExpressionBinding input, IList<DbSortClause> keys)
501 DbSortExpression retExpr = input.Sort(keys);
502 ApplySpanMapping(input.Expression, retExpr);
506 private DbExpression Skip(DbExpressionBinding input, DbExpression skipCount)
508 DbExpression retExpr = _orderByLifter.Skip(input, skipCount);
509 ApplySpanMapping(input.Expression, retExpr);
513 private DbUnionAllExpression UnionAll(DbExpression left, DbExpression right)
515 DbUnionAllExpression retExpr = left.UnionAll(right);
516 UnifySpanMappings(left, right, retExpr);
521 /// Gets the target type for a CQT cast operation.
523 /// <returns>Appropriate type usage, or null if this is a "no-op"</returns>
524 private TypeUsage GetCastTargetType(TypeUsage fromType, Type toClrType, Type fromClrType, bool preserveCastForDateTime)
526 // An inlined ObjectQuery or an IOrderedQueryable expression being cast to IQueryable for use in a sequence method is a no-op.
527 if(fromClrType != null &&
528 fromClrType.IsGenericType && toClrType.IsGenericType &&
529 (fromClrType.GetGenericTypeDefinition() == typeof(ObjectQuery<>) || fromClrType.GetGenericTypeDefinition() == typeof(IOrderedQueryable<>)) &&
530 (toClrType.GetGenericTypeDefinition() == typeof(IQueryable<>) || toClrType.GetGenericTypeDefinition() == typeof(IOrderedQueryable<>)) &&
531 fromClrType.GetGenericArguments()[0] == toClrType.GetGenericArguments()[0])
536 // If the types are the same or the fromType is assignable to toType, return null
537 // (indicating no cast is required)
539 if (TryGetValueLayerType(toClrType, out toType) && CanOmitCast(fromType, toType, preserveCastForDateTime))
544 // Check that the cast is supported and adjust the target type as necessary.
545 toType = ValidateAndAdjustCastTypes(toType, fromType, toClrType, fromClrType);
551 /// Check that the given cast specification is supported and if necessary adjust target type (for instance
552 /// add precision and scale for Integral -> Decimal casts)
554 private static TypeUsage ValidateAndAdjustCastTypes(TypeUsage toType, TypeUsage fromType, Type toClrType, Type fromClrType)
556 // only support primitives if real casting is involved
557 if (toType == null || !TypeSemantics.IsScalarType(toType) || !TypeSemantics.IsScalarType(fromType))
559 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedCast(DescribeClrType(fromClrType), DescribeClrType(toClrType)));
562 PrimitiveTypeKind fromTypeKind = Helper.AsPrimitive(fromType.EdmType).PrimitiveTypeKind;
563 PrimitiveTypeKind toTypeKind = Helper.AsPrimitive(toType.EdmType).PrimitiveTypeKind;
565 if (toTypeKind == PrimitiveTypeKind.Decimal)
567 // Can't figure out the right precision and scale for decimal, so only accept integer types
568 switch (fromTypeKind)
570 case PrimitiveTypeKind.Byte:
571 case PrimitiveTypeKind.Int16:
572 case PrimitiveTypeKind.Int32:
573 case PrimitiveTypeKind.Int64:
574 case PrimitiveTypeKind.SByte:
575 // adjust precision and scale to ensure sufficient width
576 toType = TypeUsage.CreateDecimalTypeUsage((PrimitiveType)toType.EdmType, 19, 0);
579 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedCastToDecimal);
587 /// Determines if an instance of fromType can be assigned to an instance of toType using
588 /// CLR semantics. in case of primitive type, it must rely on identity since unboxing primitive requires
589 /// exact match. for nominal types, rely on subtyping.
591 private static bool CanOmitCast(TypeUsage fromType, TypeUsage toType, bool preserveCastForDateTime)
593 bool isPrimitiveType = TypeSemantics.IsPrimitiveType(fromType);
595 //SQLBUDT #573573: This is to allow for a workaround on Katmai via explicit casting by the user.
596 // The issue is that SqlServer's type Date maps to Edm.DateTime, same as SqlServer's DateTime and SmallDateTime.
597 // However the conversion is not possible for all values of Date.
599 //Note: we could also call here TypeSemantics.IsPrimitiveType(TypeUsage type, PrimitiveTypeKind primitiveTypeKind),
600 // but that checks again whether the type is primitive
601 if (isPrimitiveType && preserveCastForDateTime && ((PrimitiveType)fromType.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.DateTime)
606 if (TypeUsageEquals(fromType, toType))
613 return fromType.EdmType.EdmEquals(toType.EdmType);
616 return TypeSemantics.IsSubTypeOf(fromType, toType);
620 /// Gets the target type for an Is or As expression.
622 /// <param name="fromType">Input type in model metadata.</param>
623 /// <param name="toClrType">Test or return type.</param>
624 /// <param name="operationType">Type of operation; used in error reporting.</param>
625 /// <param name="fromClrType">Input type in CLR metadata.</param>
626 /// <returns>Appropriate target type usage.</returns>
627 private TypeUsage GetIsOrAsTargetType(TypeUsage fromType, ExpressionType operationType, Type toClrType, Type fromClrType)
629 Debug.Assert(operationType == ExpressionType.TypeAs || operationType == ExpressionType.TypeIs);
631 // Interpret all type information
633 if (!this.TryGetValueLayerType(toClrType, out toType) ||
634 (!TypeSemantics.IsEntityType(toType) &&
635 !TypeSemantics.IsComplexType(toType)))
637 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedIsOrAs(operationType,
638 DescribeClrType(fromClrType), DescribeClrType(toClrType)));
644 // requires: inlineQuery is not null and inlineQuery is Entity-SQL query
645 // effects: interprets the given query as an inline query in the current expression and unites
646 // the current query context with the context for the inline query. If the given query specifies
647 // span information, then an entry is added to the span mapping dictionary from the CQT expression
648 // that is the root of the inline query, to the span information that was present in the inline
649 // query's Span property.
650 private DbExpression TranslateInlineQueryOfT(ObjectQuery inlineQuery)
652 if (!object.ReferenceEquals(_funcletizer.RootContext, inlineQuery.QueryState.ObjectContext))
654 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedDifferentContexts);
657 // Check if the inline query has been encountered so far. If so, we don't need to
658 // include its parameters again. We do however need to translate it to a new
659 // DbExpression instance since the expressions may be tagged with span information
660 // and we don't want to mistakenly apply the directive to the wrong part of the query.
661 if (null == _inlineEntitySqlQueries)
663 _inlineEntitySqlQueries = new HashSet<ObjectQuery>();
665 bool isNewInlineQuery = _inlineEntitySqlQueries.Add(inlineQuery);
667 // The ObjectQuery should be Entity-SQL-based at this point. All other query types are currently
669 EntitySqlQueryState esqlState = (EntitySqlQueryState)inlineQuery.QueryState;
671 // We will produce the translated expression by parsing the Entity-SQL query text.
672 DbExpression resultExpression = null;
674 // If we are not converting a compiled query, or the referenced Entity-SQL ObjectQuery
675 // does not have parameters (and so no parameter references can be in the parsed tree)
676 // then the Entity-SQL can be parsed directly using the conversion command tree.
677 ObjectParameterCollection objectParameters = inlineQuery.QueryState.Parameters;
678 if (!_funcletizer.IsCompiledQuery ||
679 objectParameters == null ||
680 objectParameters.Count == 0)
682 // Add parameters if they exist and we haven't yet encountered this inline query.
683 if (isNewInlineQuery && objectParameters != null)
685 // Copy the parameters into the aggregated parameter collection - this will result
686 // in an exception if any duplicate parameter names are encountered.
687 if (this._parameters == null)
689 this._parameters = new List<KeyValuePair<ObjectParameter, QueryParameterExpression>>();
691 foreach (ObjectParameter prm in inlineQuery.QueryState.Parameters)
693 this._parameters.Add(new KeyValuePair<ObjectParameter, QueryParameterExpression>(prm.ShallowCopy(), null));
697 resultExpression = esqlState.Parse();
701 // We are converting a compiled query and parameters are present on the referenced ObjectQuery.
702 // The set of parameters available to a compiled query is fixed (so that adding/removing parameters
703 // to/from a referenced ObjectQuery does not invalidate the compiled query's execution plan), so the
704 // referenced ObjectQuery will be fully inlined by replacing each parameter reference with a
705 // DbConstantExpression containing the value of the referenced parameter.
706 resultExpression = esqlState.Parse();
707 resultExpression = ParameterReferenceRemover.RemoveParameterReferences(resultExpression, objectParameters);
710 return resultExpression;
713 private class ParameterReferenceRemover : DefaultExpressionVisitor
715 internal static DbExpression RemoveParameterReferences(DbExpression expression, ObjectParameterCollection availableParameters)
717 ParameterReferenceRemover remover = new ParameterReferenceRemover(availableParameters);
718 return remover.VisitExpression(expression);
721 private readonly ObjectParameterCollection objectParameters;
722 private ParameterReferenceRemover(ObjectParameterCollection availableParams)
725 Debug.Assert(availableParams != null, "Parameter collection cannot be null");
727 this.objectParameters = availableParams;
730 public override DbExpression Visit(DbParameterReferenceExpression expression)
732 if (this.objectParameters.Contains(expression.ParameterName))
734 // A DbNullExpression is required for null values; DbConstantExpression otherwise.
735 ObjectParameter objParam = objectParameters[expression.ParameterName];
736 if (null == objParam.Value)
738 return DbExpressionBuilder.Null(expression.ResultType);
742 // This will throw if the value is incompatible with the result type.
743 return DbExpressionBuilder.Constant(expression.ResultType, objParam.Value);
750 // creates a CQT cast expression given the source and target CLR type
751 private DbExpression CreateCastExpression(DbExpression source, Type toClrType, Type fromClrType)
753 // see if the source can be normalized as a set
754 DbExpression setSource = NormalizeSetSource(source);
755 if (!Object.ReferenceEquals(source, setSource))
757 // if the resulting cast is a no-op (no either kind is supported
758 // for set sources), yield the source
759 if (null == GetCastTargetType(setSource.ResultType, toClrType, fromClrType, true))
765 // try to find the appropriate target target for the cast
766 TypeUsage toType = GetCastTargetType(source.ResultType, toClrType, fromClrType, true);
769 // null indicates a no-op cast (from the perspective of the model)
773 return source.CastTo(toType);
776 // Utility translator method for lambda expressions. Given a lambda expression and its translated
777 // inputs, translates the lambda expression, assuming the input is a collection
778 private DbExpression TranslateLambda(LambdaExpression lambda, DbExpression input, out DbExpressionBinding binding)
780 input = NormalizeSetSource(input);
782 // create binding context for this lambda expression
783 binding = input.BindAs(_aliasGenerator.Next());
785 return TranslateLambda(lambda, binding.Variable);
788 // Utility translator method for lambda expressions. Given a lambda expression and its translated
789 // inputs, translates the lambda expression, assuming the input is a collection
790 private DbExpression TranslateLambda(LambdaExpression lambda, DbExpression input, string bindingName, out DbExpressionBinding binding)
792 input = NormalizeSetSource(input);
794 // create binding context for this lambda expression
795 binding = input.BindAs(bindingName);
797 return TranslateLambda(lambda, binding.Variable);
800 // Utility translator method for lambda expressions that are part of group by. Given a lambda expression and its translated
801 // inputs, translates the lambda expression, assuming the input needs to be used as a grouping input
802 private DbExpression TranslateLambda(LambdaExpression lambda, DbExpression input, out DbGroupExpressionBinding binding)
804 input = NormalizeSetSource(input);
806 // create binding context for this lambda expression
807 string alias = _aliasGenerator.Next();
808 binding = input.GroupBindAs(alias, string.Format(CultureInfo.InvariantCulture, "Group{0}", alias));
810 return TranslateLambda(lambda, binding.Variable);
813 // Utility translator method for lambda expressions. Given a lambda expression and its translated
814 // inputs, translates the lambda expression
815 private DbExpression TranslateLambda(LambdaExpression lambda, DbExpression input)
817 Binding scopeBinding = new Binding(lambda.Parameters[0], input);
819 // push the binding scope
820 _bindingContext.PushBindingScope(scopeBinding);
822 // translate expression within this binding scope
824 int preValue = _ignoreInclude;
827 DbExpression result = TranslateExpression(lambda.Body);
830 Debug.Assert(preValue == _ignoreInclude);
834 _bindingContext.PopBindingScope();
839 // effects: unwraps any "structured" set sources such as IGrouping instances
840 // (which acts as both a set and a structure containing a property)
841 private DbExpression NormalizeSetSource(DbExpression input)
843 Debug.Assert(null != input);
845 // If input looks like "select x from (...) as x", rewrite it as "(...)".
846 // If input has span information attached to to it then leave it as is, otherwise
847 // span info will be lost.
849 if (input.ExpressionKind == DbExpressionKind.Project && !TryGetSpan(input, out span))
851 var project = (DbProjectExpression)input;
852 if (project.Projection == project.Input.Variable)
854 input = project.Input.Expression;
858 // determine if the lambda input is an IGrouping or EntityCollection that needs to be unwrapped
859 InitializerMetadata initializerMetadata;
860 if (InitializerMetadata.TryGetInitializerMetadata(input.ResultType, out initializerMetadata))
862 if (initializerMetadata.Kind == InitializerMetadataKind.Grouping)
864 // for group by, redirect the binding to the group (rather than the property)
865 input = input.Property(ExpressionConverter.GroupColumnName);
867 else if (initializerMetadata.Kind == InitializerMetadataKind.EntityCollection)
869 // for entity collection, redirect the binding to the children
870 input = input.Property(ExpressionConverter.EntityCollectionElementsColumnName);
876 // Given a method call expression, returns the given lambda argument (unwrapping quote or closure references where
878 private LambdaExpression GetLambdaExpression(MethodCallExpression callExpression, int argumentOrdinal)
880 Expression argument = callExpression.Arguments[argumentOrdinal];
881 return (LambdaExpression)GetLambdaExpression(argument);
884 private Expression GetLambdaExpression(Expression argument)
886 if (ExpressionType.Lambda == argument.NodeType)
890 else if (ExpressionType.Quote == argument.NodeType)
892 return GetLambdaExpression(((UnaryExpression)argument).Operand);
895 throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UnexpectedLinqLambdaExpressionFormat);
898 // Translate a LINQ expression acting as a set input to a CQT expression
899 private DbExpression TranslateSet(Expression linq)
901 return NormalizeSetSource(TranslateExpression(linq));
904 // Translate a LINQ expression to a CQT expression.
905 private DbExpression TranslateExpression(Expression linq)
907 Debug.Assert(null != linq);
910 if (!_bindingContext.TryGetBoundExpression(linq, out result))
912 // translate to a CQT expression
913 Translator translator;
914 if (s_translators.TryGetValue(linq.NodeType, out translator))
916 result = translator.Translate(this, linq);
920 throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UnknownLinqNodeType, -1,
921 linq.NodeType.ToString());
927 // Cast expression to align types between CQT and eLINQ
928 private DbExpression AlignTypes(DbExpression cqt, Type toClrType)
930 Type fromClrType = null; // not used in this code path
931 TypeUsage toType = GetCastTargetType(cqt.ResultType, toClrType, fromClrType, false);
934 return cqt.CastTo(toType);
942 // Determines whether the given type is supported for materialization
943 private void CheckInitializerType(Type type)
945 // nominal types are not supported
947 if (_funcletizer.RootContext.Perspective.TryGetType(type, out typeUsage))
949 BuiltInTypeKind typeKind = typeUsage.EdmType.BuiltInTypeKind;
950 if (BuiltInTypeKind.EntityType == typeKind ||
951 BuiltInTypeKind.ComplexType == typeKind)
953 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedNominalType(
954 typeUsage.EdmType.FullName));
958 // types implementing IEnumerable are not supported
959 if (TypeSystem.IsSequenceType(type))
961 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedEnumerableType(
962 DescribeClrType(type)));
967 // requires: Left and right are non-null.
968 // effects: Determines if the given types are equivalent, ignoring facets. In
969 // the case of primitive types, consider types equivalent if their kinds are
971 // comments: This method is useful in cases where the type facets or specific
972 // store primitive type are not reliably known, e.g. when the EDM type is determined
974 private static bool TypeUsageEquals(TypeUsage left, TypeUsage right)
976 Debug.Assert(null != left);
977 Debug.Assert(null != right);
978 if (left.EdmType.EdmEquals(right.EdmType)) { return true; }
980 // compare element types for collection
981 if (BuiltInTypeKind.CollectionType == left.EdmType.BuiltInTypeKind &&
982 BuiltInTypeKind.CollectionType == right.EdmType.BuiltInTypeKind)
984 return TypeUsageEquals(
985 ((CollectionType)left.EdmType).TypeUsage,
986 ((CollectionType)right.EdmType).TypeUsage);
989 // special case for primitive types
990 if (BuiltInTypeKind.PrimitiveType == left.EdmType.BuiltInTypeKind &&
991 BuiltInTypeKind.PrimitiveType == right.EdmType.BuiltInTypeKind)
993 // since LINQ expressions cannot indicate model types directly, we must
994 // consider types equivalent if they match on the given CLR equivalent
995 // types (consider the Xml and String primitive types)
996 return ((PrimitiveType)left.EdmType).ClrEquivalentType.Equals(
997 ((PrimitiveType)right.EdmType).ClrEquivalentType);
1003 private TypeUsage GetValueLayerType(Type linqType)
1006 if (!TryGetValueLayerType(linqType, out type))
1008 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedType(linqType));
1013 // Determine C-Space equivalent type for linqType
1014 private bool TryGetValueLayerType(Type linqType, out TypeUsage type)
1017 Type nonNullableType = TypeSystem.GetNonNullableType(linqType);
1019 // Enum types are only supported for EDM V3 and higher, do not force loading
1020 // enum types for previous versions of EDM
1021 if (nonNullableType.IsEnum && this.EdmItemCollection.EdmVersion < XmlConstants.EdmVersionForV3)
1023 nonNullableType = nonNullableType.GetEnumUnderlyingType();
1026 // See if this is a primitive type
1027 PrimitiveTypeKind primitiveTypeKind;
1028 if (ClrProviderManifest.Instance.TryGetPrimitiveTypeKind(nonNullableType, out primitiveTypeKind))
1030 type = EdmProviderManifest.Instance.GetCanonicalModelTypeUsage(primitiveTypeKind);
1034 // See if this is a collection type (if so, recursively resolve)
1035 Type elementType = TypeSystem.GetElementType(nonNullableType);
1036 if (elementType != nonNullableType)
1038 TypeUsage elementTypeUsage;
1039 if (TryGetValueLayerType(elementType, out elementTypeUsage))
1041 type = TypeHelpers.CreateCollectionTypeUsage(elementTypeUsage);
1046 // Ensure the metadata for this object type is loaded
1047 _perspective.MetadataWorkspace.ImplicitLoadAssemblyForType(linqType, null);
1049 if(!_perspective.TryGetTypeByName(nonNullableType.FullName, false, out type))
1051 // If the user is casting to a type that is not a model type or a primitive type it can be a cast to an enum that
1052 // is not in the model. In that case we use the underlying enum type.
1053 // Note that if the underlying type is not any of the EF primitive types we will fail with and InvalidCastException.
1054 // This is consistent with what we would do when seeing a cast to a primitive type that is not a EF valid primitive
1055 // type (e.g. ulong).
1056 if(nonNullableType.IsEnum && ClrProviderManifest.Instance.TryGetPrimitiveTypeKind(nonNullableType.GetEnumUnderlyingType(), out primitiveTypeKind))
1058 type = EdmProviderManifest.Instance.GetCanonicalModelTypeUsage(primitiveTypeKind);
1062 return type != null;
1066 /// Utility method validating type for comparison ops (isNull, equals, etc.).
1067 /// Only primitive types, entity types, and simple row types (no IGrouping/EntityCollection) are
1070 private static void VerifyTypeSupportedForComparison(Type clrType, TypeUsage edmType, Stack<EdmMember> memberPath)
1072 // NOTE: due to bug in null handling for complex types, complex types are currently not supported
1073 // for comparisons (see SQL BU 543956)
1074 switch (edmType.EdmType.BuiltInTypeKind)
1076 case BuiltInTypeKind.PrimitiveType:
1077 case BuiltInTypeKind.EnumType:
1078 case BuiltInTypeKind.EntityType:
1079 case BuiltInTypeKind.RefType:
1081 case BuiltInTypeKind.RowType:
1083 InitializerMetadata initializerMetadata;
1084 if (!InitializerMetadata.TryGetInitializerMetadata(edmType, out initializerMetadata) ||
1085 initializerMetadata.Kind == InitializerMetadataKind.ProjectionInitializer ||
1086 initializerMetadata.Kind == InitializerMetadataKind.ProjectionNew)
1088 VerifyRowTypeSupportedForComparison(clrType, (RowType)edmType.EdmType, memberPath);
1096 if (null == memberPath)
1098 throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedComparison(DescribeClrType(clrType)));
1102 // build up description of member path
1103 StringBuilder memberPathDescription = new StringBuilder();
1104 foreach (EdmMember member in memberPath)
1106 memberPathDescription.Append(Strings.ELinq_UnsupportedRowMemberComparison(member.Name));
1108 memberPathDescription.Append(Strings.ELinq_UnsupportedRowTypeComparison(DescribeClrType(clrType)));
1109 throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedRowComparison(memberPathDescription.ToString()));
1113 private static void VerifyRowTypeSupportedForComparison(Type clrType, RowType rowType, Stack<EdmMember> memberPath)
1115 foreach (EdmMember member in rowType.Properties)
1117 if (null == memberPath)
1119 memberPath = new Stack<EdmMember>();
1121 memberPath.Push(member);
1122 VerifyTypeSupportedForComparison(clrType, member.TypeUsage, memberPath);
1128 /// Describe type for exception message.
1130 internal static string DescribeClrType(Type clrType)
1132 string clrTypeName = clrType.Name;
1133 // Yes, this is a heuristic... just a best effort way of getting
1134 // a reasonable exception message
1135 if (IsCSharpGeneratedClass(clrTypeName, "DisplayClass") ||
1136 IsVBGeneratedClass(clrTypeName, "Closure"))
1138 return Strings.ELinq_ClosureType;
1140 if (IsCSharpGeneratedClass(clrTypeName, "AnonymousType") ||
1141 IsVBGeneratedClass(clrTypeName, "AnonymousType"))
1143 return Strings.ELinq_AnonymousType;
1146 string returnName = string.Empty;
1147 if (!String.IsNullOrEmpty(clrType.Namespace))
1149 returnName += clrType.Namespace + ".";
1151 returnName += clrType.Name;
1155 private static bool IsCSharpGeneratedClass(string typeName, string pattern)
1157 return typeName.Contains("<>") && typeName.Contains("__") && typeName.Contains(pattern);
1160 private static bool IsVBGeneratedClass(string typeName, string pattern)
1162 return typeName.Contains("_") && typeName.Contains("$") && typeName.Contains(pattern);
1166 /// Creates an implementation of IsNull. Throws exception when operand type is not supported.
1168 private DbExpression CreateIsNullExpression(DbExpression operand, Type operandClrType)
1170 VerifyTypeSupportedForComparison(operandClrType, operand.ResultType, null);
1171 return operand.IsNull();
1175 /// Creates an implementation of equals using the given pattern. Throws exception when argument types
1176 /// are not supported for equals comparison.
1178 private DbExpression CreateEqualsExpression(DbExpression left, DbExpression right, EqualsPattern pattern, Type leftClrType, Type rightClrType)
1180 VerifyTypeSupportedForComparison(leftClrType, left.ResultType, null);
1181 VerifyTypeSupportedForComparison(rightClrType, right.ResultType, null);
1183 //For Ref Type comparison, check whether they refer to compatible Entity Types.
1184 TypeUsage leftType = left.ResultType;
1185 TypeUsage rightType = right.ResultType;
1186 if (leftType.EdmType.BuiltInTypeKind == BuiltInTypeKind.RefType && rightType.EdmType.BuiltInTypeKind == BuiltInTypeKind.RefType)
1188 TypeUsage commonType;
1189 if (!TypeSemantics.TryGetCommonType(leftType, rightType, out commonType))
1191 RefType leftRefType = left.ResultType.EdmType as RefType;
1192 RefType rightRefType = right.ResultType.EdmType as RefType;
1193 throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedRefComparison(leftRefType.ElementType.FullName, rightRefType.ElementType.FullName));
1197 return RecursivelyRewriteEqualsExpression(left, right, pattern);
1199 private DbExpression RecursivelyRewriteEqualsExpression(DbExpression left, DbExpression right, EqualsPattern pattern)
1201 // check if either side is an initializer type
1202 RowType leftType = left.ResultType.EdmType as RowType;
1203 RowType rightType = left.ResultType.EdmType as RowType;
1205 if (null != leftType || null != rightType)
1207 if ((null != leftType && null != rightType) && leftType.EdmEquals(rightType))
1209 DbExpression shreddedEquals = null;
1210 // if the types are the same, use struct equivalence semantics
1211 foreach (EdmProperty property in leftType.Properties)
1213 DbPropertyExpression leftElement = left.Property(property);
1214 DbPropertyExpression rightElement = right.Property(property);
1215 DbExpression elementsEquals = RecursivelyRewriteEqualsExpression(
1216 leftElement, rightElement, pattern);
1218 // build up and expression
1219 if (null == shreddedEquals) { shreddedEquals = elementsEquals; }
1220 else { shreddedEquals = shreddedEquals.And(elementsEquals); }
1222 return shreddedEquals;
1226 // if one or both sides is an initializer and the types are not the same,
1227 // "equals" always evaluates to false
1228 return DbExpressionBuilder.False;
1233 return ImplementEquality(left, right, pattern);
1237 // For comparisons, where the left and right side are nullable or not nullable,
1238 // here are the (compositionally safe) null equality predicates:
1239 // -- x NOT NULL, y NULL
1240 // x = y AND NOT (y IS NULL)
1241 // -- x NULL, y NULL
1242 // (x = y AND (NOT (x IS NULL OR y IS NULL))) OR (x IS NULL AND y IS NULL)
1243 // -- x NOT NULL, y NOT NULL
1245 // -- x NULL, y NOT NULL
1246 // x = y AND NOT (x IS NULL)
1247 private DbExpression ImplementEquality(DbExpression left, DbExpression right, EqualsPattern pattern)
1249 switch (left.ExpressionKind)
1251 case DbExpressionKind.Constant:
1252 switch (right.ExpressionKind)
1254 case DbExpressionKind.Constant: // constant EQ constant
1255 return left.Equal(right);
1256 case DbExpressionKind.Null: // null EQ constant --> false
1257 return DbExpressionBuilder.False;
1259 return ImplementEqualityConstantAndUnknown((System.Data.Common.CommandTrees.DbConstantExpression)left, right, pattern);
1261 case DbExpressionKind.Null:
1262 switch (right.ExpressionKind)
1264 case DbExpressionKind.Constant: // null EQ constant --> false
1265 return DbExpressionBuilder.False;
1266 case DbExpressionKind.Null: // null EQ null --> true
1267 return DbExpressionBuilder.True;
1268 default: // null EQ right --> right IS NULL
1269 return right.IsNull();
1272 switch (right.ExpressionKind)
1274 case DbExpressionKind.Constant:
1275 return ImplementEqualityConstantAndUnknown((System.Data.Common.CommandTrees.DbConstantExpression)right, left, pattern);
1276 case DbExpressionKind.Null: // left EQ null --> left IS NULL
1277 return left.IsNull();
1279 return ImplementEqualityUnknownArguments(left, right, pattern);
1284 // Generate an equality expression with one unknown operator and
1285 private DbExpression ImplementEqualityConstantAndUnknown(
1286 System.Data.Common.CommandTrees.DbConstantExpression constant, DbExpression unknown, EqualsPattern pattern)
1290 case EqualsPattern.Store:
1291 case EqualsPattern.PositiveNullEqualityNonComposable: // for Joins
1292 return constant.Equal(unknown); // either both are non-null, or one is null and the predicate result is undefined
1293 case EqualsPattern.PositiveNullEqualityComposable:
1294 if (!_funcletizer.RootContext.ContextOptions.UseCSharpNullComparisonBehavior)
1296 return constant.Equal(unknown); // same as EqualsPattern.PositiveNullEqualityNonComposable
1298 return constant.Equal(unknown).And(unknown.IsNull().Not()); // add more logic to avoid undefined result for true clr semantics
1300 Debug.Fail("unknown pattern");
1305 // Generate an equality expression where the values of the left and right operands are completely unknown
1306 private DbExpression ImplementEqualityUnknownArguments(DbExpression left, DbExpression right, EqualsPattern pattern)
1310 case EqualsPattern.Store: // left EQ right
1311 return left.Equal(right);
1312 case EqualsPattern.PositiveNullEqualityNonComposable: // for Joins
1313 return left.Equal(right).Or(left.IsNull().And(right.IsNull()));
1314 case EqualsPattern.PositiveNullEqualityComposable:
1316 var bothNotNull = left.Equal(right);
1317 var bothNull = left.IsNull().And(right.IsNull());
1318 if (!_funcletizer.RootContext.ContextOptions.UseCSharpNullComparisonBehavior)
1320 return bothNotNull.Or(bothNull); // same as EqualsPattern.PositiveNullEqualityNonComposable
1322 // add more logic to avoid undefined result for true clr semantics, ensuring composability
1323 // (left EQ right AND NOT (left IS NULL OR right IS NULL)) OR (left IS NULL AND right IS NULL)
1324 var anyOneIsNull = left.IsNull().Or(right.IsNull());
1325 return (bothNotNull.And(anyOneIsNull.Not())).Or(bothNull);
1328 Debug.Fail("unexpected pattern");
1335 #region Helper Methods Shared by Translators
1337 /// Helper method for String.StartsWith, String.EndsWith and String.Contains
1339 /// object.Method(argument), where Method is one of String.StartsWith, String.EndsWith or
1340 /// String.Contains is translated into:
1341 /// 1) If argument is a constant or parameter and the provider supports escaping:
1342 /// object like ("%") + argument1 + ("%"), where argument1 is argument escaped by the provider
1343 /// and ("%") are appended on the begining/end depending on whether
1344 /// insertPercentAtStart/insertPercentAtEnd are specified
1346 /// object.Method(argument) -> defaultTranslator
1348 /// <param name="call"></param>
1349 /// <param name="insertPercentAtStart">Should '%' be inserted at the begining of the pattern</param>
1350 /// <param name="insertPercentAtEnd">Should '%' be inserted at the end of the pattern</param>
1351 /// <param name="defaultTranslator">The delegate that provides the default translation</param>
1352 /// <returns>The translation</returns>
1353 private DbExpression TranslateFunctionIntoLike(MethodCallExpression call, bool insertPercentAtStart, bool insertPercentAtEnd, Func<ExpressionConverter, MethodCallExpression, DbExpression, DbExpression, DbExpression> defaultTranslator)
1356 bool providerSupportsEscapingLikeArgument = this.ProviderManifest.SupportsEscapingLikeArgument(out escapeChar);
1357 bool useLikeTranslation = false;
1358 bool specifyEscape = true;
1360 Expression patternExpression = call.Arguments[0];
1361 Expression inputExpression = call.Object;
1363 QueryParameterExpression queryParameterExpression = patternExpression as QueryParameterExpression;
1364 if (providerSupportsEscapingLikeArgument && (queryParameterExpression != null))
1366 useLikeTranslation = true;
1367 bool specifyEscapeDummy;
1368 patternExpression = queryParameterExpression.EscapeParameterForLike(input => PreparePattern(input, insertPercentAtStart, insertPercentAtEnd, out specifyEscapeDummy));
1371 DbExpression translatedPatternExpression = this.TranslateExpression(patternExpression);
1372 DbExpression translatedInputExpression = this.TranslateExpression(inputExpression);
1374 if (providerSupportsEscapingLikeArgument && translatedPatternExpression.ExpressionKind == DbExpressionKind.Constant)
1376 useLikeTranslation = true;
1377 DbConstantExpression constantExpression = (DbConstantExpression)translatedPatternExpression;
1379 string preparedValue = PreparePattern((string)constantExpression.Value, insertPercentAtStart, insertPercentAtEnd, out specifyEscape);
1380 Debug.Assert(preparedValue != null, "The prepared value should not be null when the input is non-null");
1382 //Note: the result type needs to be taken from the original expression, as the user may have specified Unicode/Non-Unicode
1383 translatedPatternExpression = DbExpressionBuilder.Constant(constantExpression.ResultType, preparedValue);
1386 DbExpression result;
1387 if (useLikeTranslation)
1391 //DevDiv #326720: The constant expression for the escape character should not have unicode set by default
1392 var escapeExpression = DbExpressionBuilder.Constant(EdmProviderManifest.Instance.GetCanonicalModelTypeUsage(PrimitiveTypeKind.String), new String(new char[] { escapeChar }));
1393 result = DbExpressionBuilder.Like(translatedInputExpression, translatedPatternExpression, escapeExpression);
1397 result = DbExpressionBuilder.Like(translatedInputExpression, translatedPatternExpression);
1402 result = defaultTranslator(this, call, translatedPatternExpression, translatedInputExpression);
1409 /// Prepare the given input patternValue into a pattern to be used in a LIKE expression by
1410 /// first escaping it by the provider and then appending "%" and the beginging/end depending
1411 /// on whether insertPercentAtStart/insertPercentAtEnd is specified.
1413 private string PreparePattern(string patternValue, bool insertPercentAtStart, bool insertPercentAtEnd, out bool specifyEscape)
1415 // Dev10 #800466: The pattern value if originating from a parameter value could be null
1416 if (patternValue == null)
1418 specifyEscape = false;
1422 string escapedPatternValue = this.ProviderManifest.EscapeLikeArgument(patternValue);
1424 if (escapedPatternValue == null)
1426 throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.ProviderEscapeLikeArgumentReturnedNull);
1429 specifyEscape = patternValue != escapedPatternValue;
1431 System.Text.StringBuilder patternBuilder = new System.Text.StringBuilder();
1432 if (insertPercentAtStart)
1434 patternBuilder.Append("%");
1436 patternBuilder.Append(escapedPatternValue);
1437 if (insertPercentAtEnd)
1439 patternBuilder.Append("%");
1442 return patternBuilder.ToString();
1446 /// Translates the arguments into DbExpressions
1447 /// and creates a canonical function with the given functionName and these arguments
1449 /// <param name="functionName">Should represent a non-aggregate canonical function</param>
1450 /// <param name="Expression">Passed only for error handling purposes</param>
1451 /// <param name="linqArguments"></param>
1452 /// <returns></returns>
1453 private DbFunctionExpression TranslateIntoCanonicalFunction(string functionName, Expression Expression, params Expression[] linqArguments)
1455 DbExpression[] translatedArguments = new DbExpression[linqArguments.Length];
1456 for (int i = 0; i < linqArguments.Length; i++)
1458 translatedArguments[i] = TranslateExpression(linqArguments[i]);
1460 return CreateCanonicalFunction(functionName, Expression, translatedArguments);
1464 /// Creates a canonical function with the given name and the given arguments
1466 /// <param name="functionName">Should represent a non-aggregate canonical function</param>
1467 /// <param name="Expression">Passed only for error handling purposes</param>
1468 /// <param name="translatedArguments"></param>
1469 /// <returns></returns>
1470 private DbFunctionExpression CreateCanonicalFunction(string functionName, Expression Expression, params DbExpression[] translatedArguments)
1472 List<TypeUsage> translatedArgumentTypes = new List<TypeUsage>(translatedArguments.Length);
1473 foreach (DbExpression translatedArgument in translatedArguments)
1475 translatedArgumentTypes.Add(translatedArgument.ResultType);
1477 EdmFunction function = FindCanonicalFunction(functionName, translatedArgumentTypes, false /* isGroupAggregateFunction */, Expression);
1478 return function.Invoke(translatedArguments);
1482 /// Finds a canonical function with the given functionName and argumentTypes
1484 /// <param name="functionName"></param>
1485 /// <param name="argumentTypes"></param>
1486 /// <param name="isGroupAggregateFunction"></param>
1487 /// <param name="Expression"></param>
1488 /// <returns></returns>
1489 private EdmFunction FindCanonicalFunction(string functionName, IList<TypeUsage> argumentTypes, bool isGroupAggregateFunction, Expression Expression)
1491 return FindFunction(EdmNamespaceName, functionName, argumentTypes, isGroupAggregateFunction, Expression);
1495 /// Finds a function with the given namespaceName, functionName and argumentTypes
1497 /// <param name="namespaceName"></param>
1498 /// <param name="functionName"></param>
1499 /// <param name="argumentTypes"></param>
1500 /// <param name="isGroupAggregateFunction"></param>
1501 /// <param name="Expression"></param>
1502 /// <returns></returns>
1503 private EdmFunction FindFunction(string namespaceName, string functionName, IList<TypeUsage> argumentTypes, bool isGroupAggregateFunction, Expression Expression)
1505 // find the function
1506 IList<EdmFunction> candidateFunctions;
1507 if (!_perspective.TryGetFunctionByName(namespaceName, functionName, false /* ignore case */, out candidateFunctions))
1509 ThrowUnresolvableFunction(Expression);
1512 Debug.Assert(null != candidateFunctions && candidateFunctions.Count > 0, "provider functions must not be null or empty");
1515 EdmFunction function = FunctionOverloadResolver.ResolveFunctionOverloads(candidateFunctions, argumentTypes, isGroupAggregateFunction, out isAmbiguous);
1516 if (isAmbiguous || null == function)
1518 ThrowUnresolvableFunctionOverload(Expression, isAmbiguous);
1524 /// Helper method for FindFunction
1526 /// <param name="Expression"></param>
1527 private static void ThrowUnresolvableFunction(Expression Expression)
1529 if (Expression.NodeType == ExpressionType.Call)
1531 MethodInfo methodInfo = ((MethodCallExpression)Expression).Method;
1532 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableFunctionForMethod(methodInfo, methodInfo.DeclaringType));
1534 else if (Expression.NodeType == ExpressionType.MemberAccess)
1538 MemberInfo memberInfo = TypeSystem.PropertyOrField(((MemberExpression)Expression).Member, out memberName, out memberType);
1539 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableFunctionForMember(memberInfo, memberInfo.DeclaringType));
1541 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableFunctionForExpression(Expression.NodeType));
1545 /// Helper method for FindCanonicalFunction
1547 /// <param name="Expression"></param>
1548 private static void ThrowUnresolvableFunctionOverload(Expression Expression, bool isAmbiguous)
1550 if (Expression.NodeType == ExpressionType.Call)
1552 MethodInfo methodInfo = ((MethodCallExpression)Expression).Method;
1555 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableFunctionForMethodAmbiguousMatch(methodInfo, methodInfo.DeclaringType));
1559 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableFunctionForMethodNotFound(methodInfo, methodInfo.DeclaringType));
1562 else if (Expression.NodeType == ExpressionType.MemberAccess)
1566 MemberInfo memberInfo = TypeSystem.PropertyOrField(((MemberExpression)Expression).Member, out memberName, out memberType);
1567 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableStoreFunctionForMember(memberInfo, memberInfo.DeclaringType));
1569 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableStoreFunctionForExpression(Expression.NodeType));
1572 private DbNewInstanceExpression CreateNewRowExpression(List<KeyValuePair<string, DbExpression>> columns, InitializerMetadata initializerMetadata)
1574 List<DbExpression> propertyValues = new List<DbExpression>(columns.Count);
1575 List<EdmProperty> properties = new List<EdmProperty>(columns.Count);
1576 for (int i = 0; i < columns.Count; i++)
1578 var column = columns[i];
1579 propertyValues.Add(column.Value);
1580 properties.Add(new EdmProperty(column.Key, column.Value.ResultType));
1582 RowType rowType = new RowType(properties, initializerMetadata);
1583 TypeUsage typeUsage = TypeUsage.Create(rowType);
1584 return typeUsage.New(propertyValues);
1589 #region Private enums
1590 // Describes different implementation pattern for equality comparisons.
1591 // For all patterns, if one side of the expression is a constant null, converts to an IS NULL
1592 // expression (or resolves to 'true' or 'false' if some constraint is known for the other side).
1594 // If neither side is a constant null, the semantics differ:
1596 // (1) (left EQ right) => left and right are equal and not null, so return true.
1597 // (2) (left IS NULL AND right IS NULL) => Both left and right are null, so return true.
1598 // (3) NOT (left IS NULL OR right IS NULL) =>
1599 // If only one of left or right is null, (1) evaluates to "unknown" and (2) evaluates to false. So we get "unknown" from DB which is null in C#.
1600 // This is not desired as it does not help in composability. Hence, (3) is used to return false instead of "unknown" when only one of the operands is null.
1603 // PositiveNullEqualityNonComposable: (1) OR (2) - suitable only for Join operators, as they are not composable
1604 // PositiveNullEqualityComposable: (1) OR (2) AND (3)
1606 // In the actual implementation (see ImplementEquality), optimizations exist if one or the other
1607 // side is known not to be null.
1608 private enum EqualsPattern
1610 Store, // defer to store
1611 PositiveNullEqualityNonComposable, // simulate C# semantics in store, return "null" if left or right is null, but not both. Suitable for joins.
1612 PositiveNullEqualityComposable, // simulate C# semantics in store, always return true or false