1 //--------------------------------------------------------------------- 2 // <copyright file="ExpressionConverter.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 //--------------------------------------------------------------------- 8 9 namespace System.Data.Objects.ELinq 10 { 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; 22 using System.Linq; 23 using System.Linq.Expressions; 24 using System.Reflection; 25 using System.Text; 26 27 /// <summary> 28 /// Class supporting conversion of LINQ expressions to EDM CQT expressions. 29 /// </summary> 30 internal sealed partial class ExpressionConverter 31 { 32 #region Fields 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; 42 private Span _span; 43 private HashSet<ObjectQuery> _inlineEntitySqlQueries; 44 private int _ignoreInclude; 45 private readonly AliasGenerator _aliasGenerator = new AliasGenerator("LQ", 0); 46 private readonly OrderByLifter _orderByLifter; 47 48 #region Consts 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(); 52 53 internal const string s_entityCollectionCountPropertyName = "Count"; 54 internal const string s_nullableHasValuePropertyName = "HasValue"; 55 internal const string s_nullableValuePropertyName = "Value"; 56 57 /// <summary> 58 /// Gets the name of the key column appearing in ELinq GroupBy projections 59 /// </summary> 60 internal const string KeyColumnName = "Key"; 61 62 /// <summary> 63 /// Gets the name of the group column appearing in ELinq CQTs (used in GroupBy expressions) 64 /// </summary> 65 internal const string GroupColumnName = "Group"; 66 67 /// <summary> 68 /// Gets the name of the parent column appearing in ELinq EntityCollection projections 69 /// </summary> 70 internal const string EntityCollectionOwnerColumnName = "Owner"; 71 72 /// <summary> 73 /// Gets the name of the children column appearing in ELinq EntityCollection projections 74 /// </summary> 75 internal const string EntityCollectionElementsColumnName = "Elements"; 76 77 /// <summary> 78 /// The Edm namespace name, used for canonical functions 79 /// </summary> 80 internal const string EdmNamespaceName = "Edm"; 81 #endregion 82 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"; 109 #endregion 110 111 #region Additional Entity function names 112 private const string AsUnicode = "AsUnicode"; 113 private const string AsNonUnicode = "AsNonUnicode"; 114 #endregion 115 #endregion 116 117 #region Constructors and static initializors ExpressionConverter(Funcletizer funcletizer, Expression expression)118 internal ExpressionConverter(Funcletizer funcletizer, Expression expression) 119 { 120 EntityUtil.CheckArgumentNull(funcletizer, "funcletizer"); 121 EntityUtil.CheckArgumentNull(expression, "expression"); 122 123 // Funcletize the expression (identify subexpressions that should be evaluated 124 // locally) 125 _funcletizer = funcletizer; 126 expression = funcletizer.Funcletize(expression, out _recompileRequired); 127 128 // Normalize the expression (replace obfuscated parts of the tree with simpler nodes) 129 LinqExpressionNormalizer normalizer = new LinqExpressionNormalizer(); 130 _expression = normalizer.Visit(expression); 131 132 _perspective = funcletizer.RootContext.Perspective; 133 _bindingContext = new BindingContext(); 134 _ignoreInclude = 0; 135 _orderByLifter = new OrderByLifter(_aliasGenerator); 136 } 137 138 // initialize translator dictionary (which support identification of translators 139 // for LINQ expression node types) InitializeTranslators()140 private static Dictionary<ExpressionType, Translator> InitializeTranslators() 141 { 142 Dictionary<ExpressionType, Translator> translators = new Dictionary<ExpressionType, Translator>(); 143 foreach (Translator translator in GetTranslators()) 144 { 145 foreach (ExpressionType nodeType in translator.NodeTypes) 146 { 147 translators.Add(nodeType, translator); 148 } 149 } 150 151 return translators; 152 } 153 GetTranslators()154 private static IEnumerable<Translator> GetTranslators() 155 { 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); 199 } 200 #endregion 201 202 #region Properties 203 private EdmItemCollection EdmItemCollection 204 { 205 get 206 { 207 return (EdmItemCollection)_funcletizer.RootContext.MetadataWorkspace.GetItemCollection(DataSpace.CSpace, true); 208 } 209 } 210 internal DbProviderManifest ProviderManifest 211 { 212 get 213 { 214 return ((StoreItemCollection)_funcletizer.RootContext.MetadataWorkspace.GetItemCollection(DataSpace.SSpace)).StoreProviderManifest; 215 } 216 } GetParameters()217 internal System.Collections.ObjectModel.ReadOnlyCollection<KeyValuePair<ObjectParameter, QueryParameterExpression>> GetParameters() 218 { 219 if (null != _parameters) { return _parameters.AsReadOnly(); } 220 return null; 221 } 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; } } 227 #endregion 228 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. Convert()234 internal DbExpression Convert() 235 { 236 DbExpression result = this.TranslateExpression(_expression); 237 if (!TryGetSpan(result, out _span)) 238 { 239 _span = null; 240 } 241 return result; 242 } 243 CanFuncletizePropertyInfo(PropertyInfo propertyInfo)244 internal static bool CanFuncletizePropertyInfo(PropertyInfo propertyInfo) 245 { 246 return MemberAccessTranslator.CanFuncletizePropertyInfo(propertyInfo); 247 } 248 CanIncludeSpanInfo()249 internal bool CanIncludeSpanInfo() 250 { 251 return (_ignoreInclude == 0); 252 } 253 #endregion 254 255 #region Private Methods 256 NotifyMergeOption(MergeOption mergeOption)257 private void NotifyMergeOption(MergeOption mergeOption) 258 { 259 if (!this._mergeOption.HasValue) 260 { 261 this._mergeOption = mergeOption; 262 } 263 } 264 265 // Requires: metadata must not be null. 266 // 267 // Effects: adds initializer metadata to this query context. 268 // 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., 271 // 272 // outer.Join(inner, o => new Foo { X = o.ID }, i => new Foo { Y = i.ID }, ... 273 // 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: 277 // 278 // Linq: foo1 == foo2 (which presumably amounts to foo1.X == foo2.X && foo1.Y == foo2.Y 279 // ELinq: foo1.X == foo2.Y 280 // 281 // Similar problems occur with set operations such as Union and Concat, where one of the initialization 282 // patterns may be ignored. 283 // 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.")] ValidateInitializerMetadata(InitializerMetadata metadata)287 internal void ValidateInitializerMetadata(InitializerMetadata metadata) 288 { 289 Debug.Assert(null != metadata); 290 InitializerMetadata existingMetadata; 291 if (_initializers != null && _initializers.TryGetValue(metadata.ClrType, out existingMetadata)) 292 { 293 // Verify the initializers are compatible. 294 if (!metadata.Equals(existingMetadata)) 295 { 296 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedHeterogeneousInitializers( 297 ExpressionConverter.DescribeClrType(metadata.ClrType))); 298 } 299 } 300 else 301 { 302 // Register the metadata so that subsequent initializers for this type can be verified. 303 if (_initializers == null) 304 { 305 _initializers = new Dictionary<Type, InitializerMetadata>(); 306 } 307 _initializers.Add(metadata.ClrType, metadata); 308 } 309 } 310 AddParameter(QueryParameterExpression queryParameter)311 private void AddParameter(QueryParameterExpression queryParameter) 312 { 313 if (null == _parameters) 314 { 315 _parameters = new List<KeyValuePair<ObjectParameter, QueryParameterExpression>>(); 316 } 317 if (!_parameters.Select(p => p.Value).Contains(queryParameter)) 318 { 319 ObjectParameter parameter = new ObjectParameter(queryParameter.ParameterReference.ParameterName, queryParameter.Type); 320 _parameters.Add(new KeyValuePair<ObjectParameter, QueryParameterExpression>(parameter, queryParameter)); 321 } 322 } 323 IsQueryRoot(Expression Expression)324 private bool IsQueryRoot(Expression Expression) 325 { 326 // 327 // An expression is the query root if it was the expression used 328 // when constructing this converter. 329 // 330 return object.ReferenceEquals(_expression, Expression); 331 } 332 333 #region Span Mapping maintenance methods 334 335 /// <summary> 336 /// Adds a new mapping from DbExpression => Span information for the specified expression, 337 /// after first ensuring that the mapping dictionary has been instantiated. 338 /// </summary> 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. 343 /// </param> 344 /// <returns>The original <paramref name="expression"/> argument, to allow <c>return AddSpanMapping(expression, span)</c> scenarios</returns> AddSpanMapping(DbExpression expression, Span span)345 private DbExpression AddSpanMapping(DbExpression expression, Span span) 346 { 347 if (span != null && this.CanIncludeSpanInfo()) 348 { 349 if (null == _spanMappings) 350 { 351 _spanMappings = new Dictionary<DbExpression, Span>(); 352 } 353 Span storedSpan = null; 354 if (_spanMappings.TryGetValue(expression, out storedSpan)) 355 { 356 foreach (Span.SpanPath sp in span.SpanList) 357 { 358 storedSpan.AddSpanPath(sp); 359 } 360 _spanMappings[expression] = storedSpan; 361 } 362 else 363 { 364 _spanMappings[expression] = span; 365 } 366 } 367 368 return expression; 369 } 370 371 /// <summary> 372 /// Attempts to retrieve Span information for the specified DbExpression. 373 /// </summary> 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> TryGetSpan(DbExpression expression, out Span span)377 private bool TryGetSpan(DbExpression expression, out Span span) 378 { 379 if (_spanMappings != null) 380 { 381 return _spanMappings.TryGetValue(expression, out span); 382 } 383 384 span = null; 385 return false; 386 } 387 388 /// <summary> 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. 394 /// </summary> 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> ApplySpanMapping(DbExpression from, DbExpression to)397 private void ApplySpanMapping(DbExpression from, DbExpression to) 398 { 399 Span argumentSpan; 400 if (TryGetSpan(from, out argumentSpan)) 401 { 402 AddSpanMapping(to, argumentSpan); 403 } 404 } 405 406 /// <summary> 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 409 /// as follows: 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. 417 /// </summary> 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> UnifySpanMappings(DbExpression left, DbExpression right, DbExpression to)421 private void UnifySpanMappings(DbExpression left, DbExpression right, DbExpression to) 422 { 423 Span leftSpan = null; 424 Span rightSpan = null; 425 426 bool hasLeftSpan = TryGetSpan(left, out leftSpan); 427 bool hasRightSpan = TryGetSpan(right, out rightSpan); 428 if (!hasLeftSpan && !hasRightSpan) 429 { 430 return; 431 } 432 433 Debug.Assert(leftSpan != null || rightSpan != null, "Span mappings contain null?"); 434 AddSpanMapping(to, Span.CopyUnion(leftSpan, rightSpan)); 435 } 436 #endregion 437 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). 444 Distinct(DbExpression argument)445 private DbDistinctExpression Distinct(DbExpression argument) 446 { 447 DbDistinctExpression retExpr = argument.Distinct(); 448 ApplySpanMapping(argument, retExpr); 449 return retExpr; 450 } 451 Except(DbExpression left, DbExpression right)452 private DbExceptExpression Except(DbExpression left, DbExpression right) 453 { 454 DbExceptExpression retExpr = left.Except(right); 455 ApplySpanMapping(left, retExpr); 456 return retExpr; 457 } 458 Filter(DbExpressionBinding input, DbExpression predicate)459 private DbExpression Filter(DbExpressionBinding input, DbExpression predicate) 460 { 461 DbExpression retExpr = _orderByLifter.Filter(input, predicate); 462 ApplySpanMapping(input.Expression, retExpr); 463 return retExpr; 464 } 465 Intersect(DbExpression left, DbExpression right)466 private DbIntersectExpression Intersect(DbExpression left, DbExpression right) 467 { 468 DbIntersectExpression retExpr = left.Intersect(right); 469 UnifySpanMappings(left, right, retExpr); 470 return retExpr; 471 } 472 Limit(DbExpression argument, DbExpression limit)473 private DbExpression Limit(DbExpression argument, DbExpression limit) 474 { 475 DbExpression retExpr = _orderByLifter.Limit(argument, limit); 476 ApplySpanMapping(argument, retExpr); 477 return retExpr; 478 } 479 OfType(DbExpression argument, TypeUsage ofType)480 private DbExpression OfType(DbExpression argument, TypeUsage ofType) 481 { 482 DbExpression retExpr = _orderByLifter.OfType(argument, ofType); 483 ApplySpanMapping(argument, retExpr); 484 return retExpr; 485 } 486 Project(DbExpressionBinding input, DbExpression projection)487 private DbExpression Project(DbExpressionBinding input, DbExpression projection) 488 { 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)) 493 { 494 ApplySpanMapping(input.Expression, retExpr); 495 } 496 return retExpr; 497 } 498 Sort(DbExpressionBinding input, IList<DbSortClause> keys)499 private DbSortExpression Sort(DbExpressionBinding input, IList<DbSortClause> keys) 500 { 501 DbSortExpression retExpr = input.Sort(keys); 502 ApplySpanMapping(input.Expression, retExpr); 503 return retExpr; 504 } 505 Skip(DbExpressionBinding input, DbExpression skipCount)506 private DbExpression Skip(DbExpressionBinding input, DbExpression skipCount) 507 { 508 DbExpression retExpr = _orderByLifter.Skip(input, skipCount); 509 ApplySpanMapping(input.Expression, retExpr); 510 return retExpr; 511 } 512 UnionAll(DbExpression left, DbExpression right)513 private DbUnionAllExpression UnionAll(DbExpression left, DbExpression right) 514 { 515 DbUnionAllExpression retExpr = left.UnionAll(right); 516 UnifySpanMappings(left, right, retExpr); 517 return retExpr; 518 } 519 520 /// <summary> 521 /// Gets the target type for a CQT cast operation. 522 /// </summary> 523 /// <returns>Appropriate type usage, or null if this is a "no-op"</returns> GetCastTargetType(TypeUsage fromType, Type toClrType, Type fromClrType, bool preserveCastForDateTime)524 private TypeUsage GetCastTargetType(TypeUsage fromType, Type toClrType, Type fromClrType, bool preserveCastForDateTime) 525 { 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]) 532 { 533 return null; 534 } 535 536 // If the types are the same or the fromType is assignable to toType, return null 537 // (indicating no cast is required) 538 TypeUsage toType; 539 if (TryGetValueLayerType(toClrType, out toType) && CanOmitCast(fromType, toType, preserveCastForDateTime)) 540 { 541 return null; 542 } 543 544 // Check that the cast is supported and adjust the target type as necessary. 545 toType = ValidateAndAdjustCastTypes(toType, fromType, toClrType, fromClrType); 546 547 return toType; 548 } 549 550 /// <summary> 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) 553 /// </summary> ValidateAndAdjustCastTypes(TypeUsage toType, TypeUsage fromType, Type toClrType, Type fromClrType)554 private static TypeUsage ValidateAndAdjustCastTypes(TypeUsage toType, TypeUsage fromType, Type toClrType, Type fromClrType) 555 { 556 // only support primitives if real casting is involved 557 if (toType == null || !TypeSemantics.IsScalarType(toType) || !TypeSemantics.IsScalarType(fromType)) 558 { 559 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedCast(DescribeClrType(fromClrType), DescribeClrType(toClrType))); 560 } 561 562 PrimitiveTypeKind fromTypeKind = Helper.AsPrimitive(fromType.EdmType).PrimitiveTypeKind; 563 PrimitiveTypeKind toTypeKind = Helper.AsPrimitive(toType.EdmType).PrimitiveTypeKind; 564 565 if (toTypeKind == PrimitiveTypeKind.Decimal) 566 { 567 // Can't figure out the right precision and scale for decimal, so only accept integer types 568 switch (fromTypeKind) 569 { 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); 577 break; 578 default: 579 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedCastToDecimal); 580 } 581 } 582 583 return toType; 584 } 585 586 /// <summary> 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. 590 /// </summary> CanOmitCast(TypeUsage fromType, TypeUsage toType, bool preserveCastForDateTime)591 private static bool CanOmitCast(TypeUsage fromType, TypeUsage toType, bool preserveCastForDateTime) 592 { 593 bool isPrimitiveType = TypeSemantics.IsPrimitiveType(fromType); 594 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. 598 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) 602 { 603 return false; 604 } 605 606 if (TypeUsageEquals(fromType, toType)) 607 { 608 return true; 609 } 610 611 if (isPrimitiveType) 612 { 613 return fromType.EdmType.EdmEquals(toType.EdmType); 614 } 615 616 return TypeSemantics.IsSubTypeOf(fromType, toType); 617 } 618 619 /// <summary> 620 /// Gets the target type for an Is or As expression. 621 /// </summary> 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> GetIsOrAsTargetType(TypeUsage fromType, ExpressionType operationType, Type toClrType, Type fromClrType)627 private TypeUsage GetIsOrAsTargetType(TypeUsage fromType, ExpressionType operationType, Type toClrType, Type fromClrType) 628 { 629 Debug.Assert(operationType == ExpressionType.TypeAs || operationType == ExpressionType.TypeIs); 630 631 // Interpret all type information 632 TypeUsage toType; 633 if (!this.TryGetValueLayerType(toClrType, out toType) || 634 (!TypeSemantics.IsEntityType(toType) && 635 !TypeSemantics.IsComplexType(toType))) 636 { 637 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedIsOrAs(operationType, 638 DescribeClrType(fromClrType), DescribeClrType(toClrType))); 639 } 640 641 return toType; 642 } 643 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. TranslateInlineQueryOfT(ObjectQuery inlineQuery)650 private DbExpression TranslateInlineQueryOfT(ObjectQuery inlineQuery) 651 { 652 if (!object.ReferenceEquals(_funcletizer.RootContext, inlineQuery.QueryState.ObjectContext)) 653 { 654 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedDifferentContexts); 655 } 656 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) 662 { 663 _inlineEntitySqlQueries = new HashSet<ObjectQuery>(); 664 } 665 bool isNewInlineQuery = _inlineEntitySqlQueries.Add(inlineQuery); 666 667 // The ObjectQuery should be Entity-SQL-based at this point. All other query types are currently 668 // inlined. 669 EntitySqlQueryState esqlState = (EntitySqlQueryState)inlineQuery.QueryState; 670 671 // We will produce the translated expression by parsing the Entity-SQL query text. 672 DbExpression resultExpression = null; 673 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) 681 { 682 // Add parameters if they exist and we haven't yet encountered this inline query. 683 if (isNewInlineQuery && objectParameters != null) 684 { 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) 688 { 689 this._parameters = new List<KeyValuePair<ObjectParameter, QueryParameterExpression>>(); 690 } 691 foreach (ObjectParameter prm in inlineQuery.QueryState.Parameters) 692 { 693 this._parameters.Add(new KeyValuePair<ObjectParameter, QueryParameterExpression>(prm.ShallowCopy(), null)); 694 } 695 } 696 697 resultExpression = esqlState.Parse(); 698 } 699 else 700 { 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); 708 } 709 710 return resultExpression; 711 } 712 713 private class ParameterReferenceRemover : DefaultExpressionVisitor 714 { RemoveParameterReferences(DbExpression expression, ObjectParameterCollection availableParameters)715 internal static DbExpression RemoveParameterReferences(DbExpression expression, ObjectParameterCollection availableParameters) 716 { 717 ParameterReferenceRemover remover = new ParameterReferenceRemover(availableParameters); 718 return remover.VisitExpression(expression); 719 } 720 721 private readonly ObjectParameterCollection objectParameters; ParameterReferenceRemover(ObjectParameterCollection availableParams)722 private ParameterReferenceRemover(ObjectParameterCollection availableParams) 723 : base() 724 { 725 Debug.Assert(availableParams != null, "Parameter collection cannot be null"); 726 727 this.objectParameters = availableParams; 728 } 729 Visit(DbParameterReferenceExpression expression)730 public override DbExpression Visit(DbParameterReferenceExpression expression) 731 { 732 if (this.objectParameters.Contains(expression.ParameterName)) 733 { 734 // A DbNullExpression is required for null values; DbConstantExpression otherwise. 735 ObjectParameter objParam = objectParameters[expression.ParameterName]; 736 if (null == objParam.Value) 737 { 738 return DbExpressionBuilder.Null(expression.ResultType); 739 } 740 else 741 { 742 // This will throw if the value is incompatible with the result type. 743 return DbExpressionBuilder.Constant(expression.ResultType, objParam.Value); 744 } 745 } 746 return expression; 747 } 748 } 749 750 // creates a CQT cast expression given the source and target CLR type CreateCastExpression(DbExpression source, Type toClrType, Type fromClrType)751 private DbExpression CreateCastExpression(DbExpression source, Type toClrType, Type fromClrType) 752 { 753 // see if the source can be normalized as a set 754 DbExpression setSource = NormalizeSetSource(source); 755 if (!Object.ReferenceEquals(source, setSource)) 756 { 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)) 760 { 761 return source; 762 } 763 } 764 765 // try to find the appropriate target target for the cast 766 TypeUsage toType = GetCastTargetType(source.ResultType, toClrType, fromClrType, true); 767 if (null == toType) 768 { 769 // null indicates a no-op cast (from the perspective of the model) 770 return source; 771 } 772 773 return source.CastTo(toType); 774 } 775 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 TranslateLambda(LambdaExpression lambda, DbExpression input, out DbExpressionBinding binding)778 private DbExpression TranslateLambda(LambdaExpression lambda, DbExpression input, out DbExpressionBinding binding) 779 { 780 input = NormalizeSetSource(input); 781 782 // create binding context for this lambda expression 783 binding = input.BindAs(_aliasGenerator.Next()); 784 785 return TranslateLambda(lambda, binding.Variable); 786 } 787 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 TranslateLambda(LambdaExpression lambda, DbExpression input, string bindingName, out DbExpressionBinding binding)790 private DbExpression TranslateLambda(LambdaExpression lambda, DbExpression input, string bindingName, out DbExpressionBinding binding) 791 { 792 input = NormalizeSetSource(input); 793 794 // create binding context for this lambda expression 795 binding = input.BindAs(bindingName); 796 797 return TranslateLambda(lambda, binding.Variable); 798 } 799 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 TranslateLambda(LambdaExpression lambda, DbExpression input, out DbGroupExpressionBinding binding)802 private DbExpression TranslateLambda(LambdaExpression lambda, DbExpression input, out DbGroupExpressionBinding binding) 803 { 804 input = NormalizeSetSource(input); 805 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)); 809 810 return TranslateLambda(lambda, binding.Variable); 811 } 812 813 // Utility translator method for lambda expressions. Given a lambda expression and its translated 814 // inputs, translates the lambda expression TranslateLambda(LambdaExpression lambda, DbExpression input)815 private DbExpression TranslateLambda(LambdaExpression lambda, DbExpression input) 816 { 817 Binding scopeBinding = new Binding(lambda.Parameters[0], input); 818 819 // push the binding scope 820 _bindingContext.PushBindingScope(scopeBinding); 821 822 // translate expression within this binding scope 823 #if DEBUG 824 int preValue = _ignoreInclude; 825 #endif 826 _ignoreInclude++; 827 DbExpression result = TranslateExpression(lambda.Body); 828 _ignoreInclude--; 829 #if DEBUG 830 Debug.Assert(preValue == _ignoreInclude); 831 #endif 832 833 // pop binding scope 834 _bindingContext.PopBindingScope(); 835 836 return result; 837 } 838 839 // effects: unwraps any "structured" set sources such as IGrouping instances 840 // (which acts as both a set and a structure containing a property) NormalizeSetSource(DbExpression input)841 private DbExpression NormalizeSetSource(DbExpression input) 842 { 843 Debug.Assert(null != input); 844 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. 848 Span span; 849 if (input.ExpressionKind == DbExpressionKind.Project && !TryGetSpan(input, out span)) 850 { 851 var project = (DbProjectExpression)input; 852 if (project.Projection == project.Input.Variable) 853 { 854 input = project.Input.Expression; 855 } 856 } 857 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)) 861 { 862 if (initializerMetadata.Kind == InitializerMetadataKind.Grouping) 863 { 864 // for group by, redirect the binding to the group (rather than the property) 865 input = input.Property(ExpressionConverter.GroupColumnName); 866 } 867 else if (initializerMetadata.Kind == InitializerMetadataKind.EntityCollection) 868 { 869 // for entity collection, redirect the binding to the children 870 input = input.Property(ExpressionConverter.EntityCollectionElementsColumnName); 871 } 872 } 873 return input; 874 } 875 876 // Given a method call expression, returns the given lambda argument (unwrapping quote or closure references where 877 // necessary) GetLambdaExpression(MethodCallExpression callExpression, int argumentOrdinal)878 private LambdaExpression GetLambdaExpression(MethodCallExpression callExpression, int argumentOrdinal) 879 { 880 Expression argument = callExpression.Arguments[argumentOrdinal]; 881 return (LambdaExpression)GetLambdaExpression(argument); 882 } 883 GetLambdaExpression(Expression argument)884 private Expression GetLambdaExpression(Expression argument) 885 { 886 if (ExpressionType.Lambda == argument.NodeType) 887 { 888 return argument; 889 } 890 else if (ExpressionType.Quote == argument.NodeType) 891 { 892 return GetLambdaExpression(((UnaryExpression)argument).Operand); 893 } 894 895 throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UnexpectedLinqLambdaExpressionFormat); 896 } 897 898 // Translate a LINQ expression acting as a set input to a CQT expression TranslateSet(Expression linq)899 private DbExpression TranslateSet(Expression linq) 900 { 901 return NormalizeSetSource(TranslateExpression(linq)); 902 } 903 904 // Translate a LINQ expression to a CQT expression. TranslateExpression(Expression linq)905 private DbExpression TranslateExpression(Expression linq) 906 { 907 Debug.Assert(null != linq); 908 909 DbExpression result; 910 if (!_bindingContext.TryGetBoundExpression(linq, out result)) 911 { 912 // translate to a CQT expression 913 Translator translator; 914 if (s_translators.TryGetValue(linq.NodeType, out translator)) 915 { 916 result = translator.Translate(this, linq); 917 } 918 else 919 { 920 throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UnknownLinqNodeType, -1, 921 linq.NodeType.ToString()); 922 } 923 } 924 return result; 925 } 926 927 // Cast expression to align types between CQT and eLINQ AlignTypes(DbExpression cqt, Type toClrType)928 private DbExpression AlignTypes(DbExpression cqt, Type toClrType) 929 { 930 Type fromClrType = null; // not used in this code path 931 TypeUsage toType = GetCastTargetType(cqt.ResultType, toClrType, fromClrType, false); 932 if (null != toType) 933 { 934 return cqt.CastTo(toType); 935 } 936 else 937 { 938 return cqt; 939 } 940 } 941 942 // Determines whether the given type is supported for materialization CheckInitializerType(Type type)943 private void CheckInitializerType(Type type) 944 { 945 // nominal types are not supported 946 TypeUsage typeUsage; 947 if (_funcletizer.RootContext.Perspective.TryGetType(type, out typeUsage)) 948 { 949 BuiltInTypeKind typeKind = typeUsage.EdmType.BuiltInTypeKind; 950 if (BuiltInTypeKind.EntityType == typeKind || 951 BuiltInTypeKind.ComplexType == typeKind) 952 { 953 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedNominalType( 954 typeUsage.EdmType.FullName)); 955 } 956 } 957 958 // types implementing IEnumerable are not supported 959 if (TypeSystem.IsSequenceType(type)) 960 { 961 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedEnumerableType( 962 DescribeClrType(type))); 963 } 964 } 965 966 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 970 // equivalent. 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 973 // from the CLR type TypeUsageEquals(TypeUsage left, TypeUsage right)974 private static bool TypeUsageEquals(TypeUsage left, TypeUsage right) 975 { 976 Debug.Assert(null != left); 977 Debug.Assert(null != right); 978 if (left.EdmType.EdmEquals(right.EdmType)) { return true; } 979 980 // compare element types for collection 981 if (BuiltInTypeKind.CollectionType == left.EdmType.BuiltInTypeKind && 982 BuiltInTypeKind.CollectionType == right.EdmType.BuiltInTypeKind) 983 { 984 return TypeUsageEquals( 985 ((CollectionType)left.EdmType).TypeUsage, 986 ((CollectionType)right.EdmType).TypeUsage); 987 } 988 989 // special case for primitive types 990 if (BuiltInTypeKind.PrimitiveType == left.EdmType.BuiltInTypeKind && 991 BuiltInTypeKind.PrimitiveType == right.EdmType.BuiltInTypeKind) 992 { 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); 998 } 999 1000 return false; 1001 } 1002 GetValueLayerType(Type linqType)1003 private TypeUsage GetValueLayerType(Type linqType) 1004 { 1005 TypeUsage type; 1006 if (!TryGetValueLayerType(linqType, out type)) 1007 { 1008 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedType(linqType)); 1009 } 1010 return type; 1011 } 1012 1013 // Determine C-Space equivalent type for linqType TryGetValueLayerType(Type linqType, out TypeUsage type)1014 private bool TryGetValueLayerType(Type linqType, out TypeUsage type) 1015 { 1016 // Remove nullable 1017 Type nonNullableType = TypeSystem.GetNonNullableType(linqType); 1018 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) 1022 { 1023 nonNullableType = nonNullableType.GetEnumUnderlyingType(); 1024 } 1025 1026 // See if this is a primitive type 1027 PrimitiveTypeKind primitiveTypeKind; 1028 if (ClrProviderManifest.Instance.TryGetPrimitiveTypeKind(nonNullableType, out primitiveTypeKind)) 1029 { 1030 type = EdmProviderManifest.Instance.GetCanonicalModelTypeUsage(primitiveTypeKind); 1031 return true; 1032 } 1033 1034 // See if this is a collection type (if so, recursively resolve) 1035 Type elementType = TypeSystem.GetElementType(nonNullableType); 1036 if (elementType != nonNullableType) 1037 { 1038 TypeUsage elementTypeUsage; 1039 if (TryGetValueLayerType(elementType, out elementTypeUsage)) 1040 { 1041 type = TypeHelpers.CreateCollectionTypeUsage(elementTypeUsage); 1042 return true; 1043 } 1044 } 1045 1046 // Ensure the metadata for this object type is loaded 1047 _perspective.MetadataWorkspace.ImplicitLoadAssemblyForType(linqType, null); 1048 1049 if(!_perspective.TryGetTypeByName(nonNullableType.FullName, false, out type)) 1050 { 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)) 1057 { 1058 type = EdmProviderManifest.Instance.GetCanonicalModelTypeUsage(primitiveTypeKind); 1059 } 1060 } 1061 1062 return type != null; 1063 } 1064 1065 /// <summary> 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 1068 /// supported. 1069 /// </summary> VerifyTypeSupportedForComparison(Type clrType, TypeUsage edmType, Stack<EdmMember> memberPath)1070 private static void VerifyTypeSupportedForComparison(Type clrType, TypeUsage edmType, Stack<EdmMember> memberPath) 1071 { 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) 1075 { 1076 case BuiltInTypeKind.PrimitiveType: 1077 case BuiltInTypeKind.EnumType: 1078 case BuiltInTypeKind.EntityType: 1079 case BuiltInTypeKind.RefType: 1080 return; 1081 case BuiltInTypeKind.RowType: 1082 { 1083 InitializerMetadata initializerMetadata; 1084 if (!InitializerMetadata.TryGetInitializerMetadata(edmType, out initializerMetadata) || 1085 initializerMetadata.Kind == InitializerMetadataKind.ProjectionInitializer || 1086 initializerMetadata.Kind == InitializerMetadataKind.ProjectionNew) 1087 { 1088 VerifyRowTypeSupportedForComparison(clrType, (RowType)edmType.EdmType, memberPath); 1089 return; 1090 } 1091 break; 1092 } 1093 default: 1094 break; 1095 } 1096 if (null == memberPath) 1097 { 1098 throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedComparison(DescribeClrType(clrType))); 1099 } 1100 else 1101 { 1102 // build up description of member path 1103 StringBuilder memberPathDescription = new StringBuilder(); 1104 foreach (EdmMember member in memberPath) 1105 { 1106 memberPathDescription.Append(Strings.ELinq_UnsupportedRowMemberComparison(member.Name)); 1107 } 1108 memberPathDescription.Append(Strings.ELinq_UnsupportedRowTypeComparison(DescribeClrType(clrType))); 1109 throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedRowComparison(memberPathDescription.ToString())); 1110 } 1111 } 1112 VerifyRowTypeSupportedForComparison(Type clrType, RowType rowType, Stack<EdmMember> memberPath)1113 private static void VerifyRowTypeSupportedForComparison(Type clrType, RowType rowType, Stack<EdmMember> memberPath) 1114 { 1115 foreach (EdmMember member in rowType.Properties) 1116 { 1117 if (null == memberPath) 1118 { 1119 memberPath = new Stack<EdmMember>(); 1120 } 1121 memberPath.Push(member); 1122 VerifyTypeSupportedForComparison(clrType, member.TypeUsage, memberPath); 1123 memberPath.Pop(); 1124 } 1125 } 1126 1127 /// <summary> 1128 /// Describe type for exception message. 1129 /// </summary> DescribeClrType(Type clrType)1130 internal static string DescribeClrType(Type clrType) 1131 { 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")) 1137 { 1138 return Strings.ELinq_ClosureType; 1139 } 1140 if (IsCSharpGeneratedClass(clrTypeName, "AnonymousType") || 1141 IsVBGeneratedClass(clrTypeName, "AnonymousType")) 1142 { 1143 return Strings.ELinq_AnonymousType; 1144 } 1145 1146 string returnName = string.Empty; 1147 if (!String.IsNullOrEmpty(clrType.Namespace)) 1148 { 1149 returnName += clrType.Namespace + "."; 1150 } 1151 returnName += clrType.Name; 1152 return returnName; 1153 } 1154 IsCSharpGeneratedClass(string typeName, string pattern)1155 private static bool IsCSharpGeneratedClass(string typeName, string pattern) 1156 { 1157 return typeName.Contains("<>") && typeName.Contains("__") && typeName.Contains(pattern); 1158 } 1159 IsVBGeneratedClass(string typeName, string pattern)1160 private static bool IsVBGeneratedClass(string typeName, string pattern) 1161 { 1162 return typeName.Contains("_") && typeName.Contains("$") && typeName.Contains(pattern); 1163 } 1164 1165 /// <summary> 1166 /// Creates an implementation of IsNull. Throws exception when operand type is not supported. 1167 /// </summary> CreateIsNullExpression(DbExpression operand, Type operandClrType)1168 private DbExpression CreateIsNullExpression(DbExpression operand, Type operandClrType) 1169 { 1170 VerifyTypeSupportedForComparison(operandClrType, operand.ResultType, null); 1171 return operand.IsNull(); 1172 } 1173 1174 /// <summary> 1175 /// Creates an implementation of equals using the given pattern. Throws exception when argument types 1176 /// are not supported for equals comparison. 1177 /// </summary> CreateEqualsExpression(DbExpression left, DbExpression right, EqualsPattern pattern, Type leftClrType, Type rightClrType)1178 private DbExpression CreateEqualsExpression(DbExpression left, DbExpression right, EqualsPattern pattern, Type leftClrType, Type rightClrType) 1179 { 1180 VerifyTypeSupportedForComparison(leftClrType, left.ResultType, null); 1181 VerifyTypeSupportedForComparison(rightClrType, right.ResultType, null); 1182 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) 1187 { 1188 TypeUsage commonType; 1189 if (!TypeSemantics.TryGetCommonType(leftType, rightType, out commonType)) 1190 { 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)); 1194 } 1195 } 1196 1197 return RecursivelyRewriteEqualsExpression(left, right, pattern); 1198 } RecursivelyRewriteEqualsExpression(DbExpression left, DbExpression right, EqualsPattern pattern)1199 private DbExpression RecursivelyRewriteEqualsExpression(DbExpression left, DbExpression right, EqualsPattern pattern) 1200 { 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; 1204 1205 if (null != leftType || null != rightType) 1206 { 1207 if ((null != leftType && null != rightType) && leftType.EdmEquals(rightType)) 1208 { 1209 DbExpression shreddedEquals = null; 1210 // if the types are the same, use struct equivalence semantics 1211 foreach (EdmProperty property in leftType.Properties) 1212 { 1213 DbPropertyExpression leftElement = left.Property(property); 1214 DbPropertyExpression rightElement = right.Property(property); 1215 DbExpression elementsEquals = RecursivelyRewriteEqualsExpression( 1216 leftElement, rightElement, pattern); 1217 1218 // build up and expression 1219 if (null == shreddedEquals) { shreddedEquals = elementsEquals; } 1220 else { shreddedEquals = shreddedEquals.And(elementsEquals); } 1221 } 1222 return shreddedEquals; 1223 } 1224 else 1225 { 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; 1229 } 1230 } 1231 else 1232 { 1233 return ImplementEquality(left, right, pattern); 1234 } 1235 } 1236 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 1244 // x = y 1245 // -- x NULL, y NOT NULL 1246 // x = y AND NOT (x IS NULL) ImplementEquality(DbExpression left, DbExpression right, EqualsPattern pattern)1247 private DbExpression ImplementEquality(DbExpression left, DbExpression right, EqualsPattern pattern) 1248 { 1249 switch (left.ExpressionKind) 1250 { 1251 case DbExpressionKind.Constant: 1252 switch (right.ExpressionKind) 1253 { 1254 case DbExpressionKind.Constant: // constant EQ constant 1255 return left.Equal(right); 1256 case DbExpressionKind.Null: // null EQ constant --> false 1257 return DbExpressionBuilder.False; 1258 default: 1259 return ImplementEqualityConstantAndUnknown((System.Data.Common.CommandTrees.DbConstantExpression)left, right, pattern); 1260 } 1261 case DbExpressionKind.Null: 1262 switch (right.ExpressionKind) 1263 { 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(); 1270 } 1271 default: // unknown 1272 switch (right.ExpressionKind) 1273 { 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(); 1278 default: 1279 return ImplementEqualityUnknownArguments(left, right, pattern); 1280 } 1281 } 1282 } 1283 1284 // Generate an equality expression with one unknown operator and ImplementEqualityConstantAndUnknown( System.Data.Common.CommandTrees.DbConstantExpression constant, DbExpression unknown, EqualsPattern pattern)1285 private DbExpression ImplementEqualityConstantAndUnknown( 1286 System.Data.Common.CommandTrees.DbConstantExpression constant, DbExpression unknown, EqualsPattern pattern) 1287 { 1288 switch (pattern) 1289 { 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) 1295 { 1296 return constant.Equal(unknown); // same as EqualsPattern.PositiveNullEqualityNonComposable 1297 } 1298 return constant.Equal(unknown).And(unknown.IsNull().Not()); // add more logic to avoid undefined result for true clr semantics 1299 default: 1300 Debug.Fail("unknown pattern"); 1301 return null; 1302 } 1303 } 1304 1305 // Generate an equality expression where the values of the left and right operands are completely unknown ImplementEqualityUnknownArguments(DbExpression left, DbExpression right, EqualsPattern pattern)1306 private DbExpression ImplementEqualityUnknownArguments(DbExpression left, DbExpression right, EqualsPattern pattern) 1307 { 1308 switch (pattern) 1309 { 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: 1315 { 1316 var bothNotNull = left.Equal(right); 1317 var bothNull = left.IsNull().And(right.IsNull()); 1318 if (!_funcletizer.RootContext.ContextOptions.UseCSharpNullComparisonBehavior) 1319 { 1320 return bothNotNull.Or(bothNull); // same as EqualsPattern.PositiveNullEqualityNonComposable 1321 } 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); 1326 } 1327 default: 1328 Debug.Fail("unexpected pattern"); 1329 return null; 1330 } 1331 } 1332 1333 #endregion 1334 1335 #region Helper Methods Shared by Translators 1336 /// <summary> 1337 /// Helper method for String.StartsWith, String.EndsWith and String.Contains 1338 /// 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 1345 /// 2) Otherwise: 1346 /// object.Method(argument) -> defaultTranslator 1347 /// </summary> 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> TranslateFunctionIntoLike(MethodCallExpression call, bool insertPercentAtStart, bool insertPercentAtEnd, Func<ExpressionConverter, MethodCallExpression, DbExpression, DbExpression, DbExpression> defaultTranslator)1353 private DbExpression TranslateFunctionIntoLike(MethodCallExpression call, bool insertPercentAtStart, bool insertPercentAtEnd, Func<ExpressionConverter, MethodCallExpression, DbExpression, DbExpression, DbExpression> defaultTranslator) 1354 { 1355 char escapeChar; 1356 bool providerSupportsEscapingLikeArgument = this.ProviderManifest.SupportsEscapingLikeArgument(out escapeChar); 1357 bool useLikeTranslation = false; 1358 bool specifyEscape = true; 1359 1360 Expression patternExpression = call.Arguments[0]; 1361 Expression inputExpression = call.Object; 1362 1363 QueryParameterExpression queryParameterExpression = patternExpression as QueryParameterExpression; 1364 if (providerSupportsEscapingLikeArgument && (queryParameterExpression != null)) 1365 { 1366 useLikeTranslation = true; 1367 bool specifyEscapeDummy; 1368 patternExpression = queryParameterExpression.EscapeParameterForLike(input => PreparePattern(input, insertPercentAtStart, insertPercentAtEnd, out specifyEscapeDummy)); 1369 } 1370 1371 DbExpression translatedPatternExpression = this.TranslateExpression(patternExpression); 1372 DbExpression translatedInputExpression = this.TranslateExpression(inputExpression); 1373 1374 if (providerSupportsEscapingLikeArgument && translatedPatternExpression.ExpressionKind == DbExpressionKind.Constant) 1375 { 1376 useLikeTranslation = true; 1377 DbConstantExpression constantExpression = (DbConstantExpression)translatedPatternExpression; 1378 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"); 1381 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); 1384 } 1385 1386 DbExpression result; 1387 if (useLikeTranslation) 1388 { 1389 if (specifyEscape) 1390 { 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); 1394 } 1395 else 1396 { 1397 result = DbExpressionBuilder.Like(translatedInputExpression, translatedPatternExpression); 1398 } 1399 } 1400 else 1401 { 1402 result = defaultTranslator(this, call, translatedPatternExpression, translatedInputExpression); 1403 } 1404 1405 return result; 1406 } 1407 1408 /// <summary> 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. 1412 /// </summary> PreparePattern(string patternValue, bool insertPercentAtStart, bool insertPercentAtEnd, out bool specifyEscape)1413 private string PreparePattern(string patternValue, bool insertPercentAtStart, bool insertPercentAtEnd, out bool specifyEscape) 1414 { 1415 // Dev10 #800466: The pattern value if originating from a parameter value could be null 1416 if (patternValue == null) 1417 { 1418 specifyEscape = false; 1419 return null; 1420 } 1421 1422 string escapedPatternValue = this.ProviderManifest.EscapeLikeArgument(patternValue); 1423 1424 if (escapedPatternValue == null) 1425 { 1426 throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.ProviderEscapeLikeArgumentReturnedNull); 1427 } 1428 1429 specifyEscape = patternValue != escapedPatternValue; 1430 1431 System.Text.StringBuilder patternBuilder = new System.Text.StringBuilder(); 1432 if (insertPercentAtStart) 1433 { 1434 patternBuilder.Append("%"); 1435 } 1436 patternBuilder.Append(escapedPatternValue); 1437 if (insertPercentAtEnd) 1438 { 1439 patternBuilder.Append("%"); 1440 } 1441 1442 return patternBuilder.ToString(); 1443 } 1444 1445 /// <summary> 1446 /// Translates the arguments into DbExpressions 1447 /// and creates a canonical function with the given functionName and these arguments 1448 /// </summary> 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> TranslateIntoCanonicalFunction(string functionName, Expression Expression, params Expression[] linqArguments)1453 private DbFunctionExpression TranslateIntoCanonicalFunction(string functionName, Expression Expression, params Expression[] linqArguments) 1454 { 1455 DbExpression[] translatedArguments = new DbExpression[linqArguments.Length]; 1456 for (int i = 0; i < linqArguments.Length; i++) 1457 { 1458 translatedArguments[i] = TranslateExpression(linqArguments[i]); 1459 } 1460 return CreateCanonicalFunction(functionName, Expression, translatedArguments); 1461 } 1462 1463 /// <summary> 1464 /// Creates a canonical function with the given name and the given arguments 1465 /// </summary> 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> CreateCanonicalFunction(string functionName, Expression Expression, params DbExpression[] translatedArguments)1470 private DbFunctionExpression CreateCanonicalFunction(string functionName, Expression Expression, params DbExpression[] translatedArguments) 1471 { 1472 List<TypeUsage> translatedArgumentTypes = new List<TypeUsage>(translatedArguments.Length); 1473 foreach (DbExpression translatedArgument in translatedArguments) 1474 { 1475 translatedArgumentTypes.Add(translatedArgument.ResultType); 1476 } 1477 EdmFunction function = FindCanonicalFunction(functionName, translatedArgumentTypes, false /* isGroupAggregateFunction */, Expression); 1478 return function.Invoke(translatedArguments); 1479 } 1480 1481 /// <summary> 1482 /// Finds a canonical function with the given functionName and argumentTypes 1483 /// </summary> 1484 /// <param name="functionName"></param> 1485 /// <param name="argumentTypes"></param> 1486 /// <param name="isGroupAggregateFunction"></param> 1487 /// <param name="Expression"></param> 1488 /// <returns></returns> FindCanonicalFunction(string functionName, IList<TypeUsage> argumentTypes, bool isGroupAggregateFunction, Expression Expression)1489 private EdmFunction FindCanonicalFunction(string functionName, IList<TypeUsage> argumentTypes, bool isGroupAggregateFunction, Expression Expression) 1490 { 1491 return FindFunction(EdmNamespaceName, functionName, argumentTypes, isGroupAggregateFunction, Expression); 1492 } 1493 1494 /// <summary> 1495 /// Finds a function with the given namespaceName, functionName and argumentTypes 1496 /// </summary> 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> FindFunction(string namespaceName, string functionName, IList<TypeUsage> argumentTypes, bool isGroupAggregateFunction, Expression Expression)1503 private EdmFunction FindFunction(string namespaceName, string functionName, IList<TypeUsage> argumentTypes, bool isGroupAggregateFunction, Expression Expression) 1504 { 1505 // find the function 1506 IList<EdmFunction> candidateFunctions; 1507 if (!_perspective.TryGetFunctionByName(namespaceName, functionName, false /* ignore case */, out candidateFunctions)) 1508 { 1509 ThrowUnresolvableFunction(Expression); 1510 } 1511 1512 Debug.Assert(null != candidateFunctions && candidateFunctions.Count > 0, "provider functions must not be null or empty"); 1513 1514 bool isAmbiguous; 1515 EdmFunction function = FunctionOverloadResolver.ResolveFunctionOverloads(candidateFunctions, argumentTypes, isGroupAggregateFunction, out isAmbiguous); 1516 if (isAmbiguous || null == function) 1517 { 1518 ThrowUnresolvableFunctionOverload(Expression, isAmbiguous); 1519 } 1520 return function; 1521 } 1522 1523 /// <summary> 1524 /// Helper method for FindFunction 1525 /// </summary> 1526 /// <param name="Expression"></param> ThrowUnresolvableFunction(Expression Expression)1527 private static void ThrowUnresolvableFunction(Expression Expression) 1528 { 1529 if (Expression.NodeType == ExpressionType.Call) 1530 { 1531 MethodInfo methodInfo = ((MethodCallExpression)Expression).Method; 1532 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableFunctionForMethod(methodInfo, methodInfo.DeclaringType)); 1533 } 1534 else if (Expression.NodeType == ExpressionType.MemberAccess) 1535 { 1536 string memberName; 1537 Type memberType; 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)); 1540 } 1541 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableFunctionForExpression(Expression.NodeType)); 1542 } 1543 1544 /// <summary> 1545 /// Helper method for FindCanonicalFunction 1546 /// </summary> 1547 /// <param name="Expression"></param> ThrowUnresolvableFunctionOverload(Expression Expression, bool isAmbiguous)1548 private static void ThrowUnresolvableFunctionOverload(Expression Expression, bool isAmbiguous) 1549 { 1550 if (Expression.NodeType == ExpressionType.Call) 1551 { 1552 MethodInfo methodInfo = ((MethodCallExpression)Expression).Method; 1553 if (isAmbiguous) 1554 { 1555 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableFunctionForMethodAmbiguousMatch(methodInfo, methodInfo.DeclaringType)); 1556 } 1557 else 1558 { 1559 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableFunctionForMethodNotFound(methodInfo, methodInfo.DeclaringType)); 1560 } 1561 } 1562 else if (Expression.NodeType == ExpressionType.MemberAccess) 1563 { 1564 string memberName; 1565 Type memberType; 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)); 1568 } 1569 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableStoreFunctionForExpression(Expression.NodeType)); 1570 } 1571 CreateNewRowExpression(List<KeyValuePair<string, DbExpression>> columns, InitializerMetadata initializerMetadata)1572 private DbNewInstanceExpression CreateNewRowExpression(List<KeyValuePair<string, DbExpression>> columns, InitializerMetadata initializerMetadata) 1573 { 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++) 1577 { 1578 var column = columns[i]; 1579 propertyValues.Add(column.Value); 1580 properties.Add(new EdmProperty(column.Key, column.Value.ResultType)); 1581 } 1582 RowType rowType = new RowType(properties, initializerMetadata); 1583 TypeUsage typeUsage = TypeUsage.Create(rowType); 1584 return typeUsage.New(propertyValues); 1585 } 1586 1587 #endregion 1588 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). 1593 // 1594 // If neither side is a constant null, the semantics differ: 1595 // 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. 1601 // 1602 // Store: (1) 1603 // PositiveNullEqualityNonComposable: (1) OR (2) - suitable only for Join operators, as they are not composable 1604 // PositiveNullEqualityComposable: (1) OR (2) AND (3) 1605 // 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 1609 { 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 1613 } 1614 #endregion 1615 } 1616 } 1617