1 //---------------------------------------------------------------------
2 // <copyright file="Translator.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // @owner Microsoft, Microsoft
7 //---------------------------------------------------------------------
9 namespace System.Data.Objects.ELinq
11 using System.Collections.Generic;
12 using System.Data.Common;
13 using System.Data.Common.CommandTrees;
14 using System.Data.Common.CommandTrees.ExpressionBuilder;
15 using System.Data.Entity;
16 using System.Data.Metadata.Edm;
17 using System.Data.Objects.DataClasses;
18 using System.Diagnostics;
19 using System.Globalization;
21 using System.Linq.Expressions;
22 using System.Reflection;
24 internal sealed partial class ExpressionConverter
26 // Base class supporting the translation of LINQ node type(s) given a LINQ expression
27 // of that type, and the "parent" translation context (the ExpressionConverter processor)
28 private abstract class Translator
30 private readonly ExpressionType[] _nodeTypes;
31 protected Translator(params ExpressionType[] nodeTypes)
33 _nodeTypes = nodeTypes;
35 // Gets LINQ node types this translator should be registed to process.
36 internal IEnumerable<ExpressionType> NodeTypes { get { return _nodeTypes; } }
37 internal abstract DbExpression Translate(ExpressionConverter parent, Expression linq);
38 public override string ToString()
40 return this.GetType().Name;
45 // Typed version of Translator
46 private abstract class TypedTranslator<T_Linq> : Translator
47 where T_Linq : Expression
49 protected TypedTranslator(params ExpressionType[] nodeTypes)
51 internal override DbExpression Translate(ExpressionConverter parent, Expression linq)
53 return TypedTranslate(parent, (T_Linq)linq);
55 protected abstract DbExpression TypedTranslate(ExpressionConverter parent, T_Linq linq);
57 private sealed class ConstantTranslator
58 : TypedTranslator<System.Linq.Expressions.ConstantExpression>
60 internal ConstantTranslator()
61 : base(ExpressionType.Constant) { }
62 protected override DbExpression TypedTranslate(ExpressionConverter parent, ConstantExpression linq)
64 // Check to see if this constant corresponds to the compiled query context parameter (it
65 // gets turned into a constant during funcletization and has special error handling).
66 if (linq == parent._funcletizer.RootContextExpression)
68 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ELinq_UnsupportedUseOfContextParameter(
69 parent._funcletizer.RootContextParameter.Name));
72 ObjectQuery queryOfT = linq.Value as ObjectQuery;
75 return parent.TranslateInlineQueryOfT(queryOfT);
78 // If it is something we can enumerate then we can evaluate locally and send to the server
79 var values = linq.Value as System.Collections.IEnumerable;
82 Type elementType = TypeSystem.GetElementType(linq.Type);
83 if ((elementType != null) && (elementType != linq.Type))
85 var expressions = new List<Expression>();
86 foreach (object o in values)
88 expressions.Add(Expression.Constant(o, elementType));
91 // Invalidate the query plan every time the query is executed since it is possible
92 // to modify an element of a collection without changing the reference.
93 parent._recompileRequired = () => true;
95 return parent.TranslateExpression(Expression.NewArrayInit(elementType, expressions));
99 bool isNullValue = null == linq.Value;
101 // Remove facet information: null instances do not constrain type facets (e.g. a null string does not restrict
102 // "length" in compatibility checks)
104 bool typeSupported = false;
105 if (parent.TryGetValueLayerType(linq.Type, out type))
107 // For constant values, support only primitive and enum type (this is all that is supported by CQTs)
108 // For null types, also allow EntityType. Although other types claim to be supported, they
109 // don't work (e.g. complex type, see SQL BU 543956)
110 if (Helper.IsScalarType(type.EdmType) ||
111 (isNullValue && Helper.IsEntityType(type.EdmType)))
113 typeSupported = true;
121 throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedNullConstant(DescribeClrType(linq.Type)));
125 throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedConstant(DescribeClrType(linq.Type)));
129 // create a constant or null expression depending on value
132 return DbExpressionBuilder.Null(type);
136 // By default use the value specified in the ConstantExpression.Value property. However,
137 // if the value was of an enum type that is not in the model its type was converted
138 // to the EdmType type corresponding to the underlying type of the enum type. In this case
139 // we also need to cast the value to the same type to avoid mismatches.
140 var value = linq.Value;
141 if (Helper.IsPrimitiveType(type.EdmType))
143 var nonNullableLinqType = TypeSystem.GetNonNullableType(linq.Type);
144 if (nonNullableLinqType.IsEnum)
146 value = System.Convert.ChangeType(linq.Value, nonNullableLinqType.GetEnumUnderlyingType(), CultureInfo.InvariantCulture);
150 return DbExpressionBuilder.Constant(type, value);
154 private sealed partial class MemberAccessTranslator
155 : TypedTranslator<MemberExpression>
157 internal MemberAccessTranslator()
158 : base(ExpressionType.MemberAccess) { }
159 // attempt to translate the member access to a "regular" property, a navigation property, or a calculated
161 protected override DbExpression TypedTranslate(ExpressionConverter parent, MemberExpression linq)
163 DbExpression propertyExpression;
166 MemberInfo memberInfo = TypeSystem.PropertyOrField(linq.Member, out memberName, out memberType);
168 // note: we check for "regular" properties last, since the other two flavors derive
170 if (linq.Expression != null)
172 DbExpression instance = parent.TranslateExpression(linq.Expression);
173 if (TryResolveAsProperty(parent, memberInfo,
174 instance.ResultType, instance, out propertyExpression))
176 return propertyExpression;
180 if (memberInfo.MemberType == MemberTypes.Property)
182 // Check whether it is one of the special properties that we know how to translate
183 PropertyTranslator propertyTranslator;
184 if (TryGetTranslator((PropertyInfo)memberInfo, out propertyTranslator))
186 return propertyTranslator.Translate(parent, linq);
190 // no other property types are supported by LINQ over entities
191 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnrecognizedMember(linq.Member.Name));
194 #region Static members and initializers
195 private static readonly Dictionary<PropertyInfo, PropertyTranslator> s_propertyTranslators;
196 private static bool s_vbPropertiesInitialized;
197 private static readonly object s_vbInitializerLock = new object();
199 static MemberAccessTranslator()
201 // initialize translators for specific properties
202 s_propertyTranslators = new Dictionary<PropertyInfo, PropertyTranslator>();
203 foreach (PropertyTranslator translator in GetPropertyTranslators())
205 foreach (PropertyInfo property in translator.Properties)
207 s_propertyTranslators.Add(property, translator);
213 /// Tries to get a translator for the given property info.
214 /// If the given property info corresponds to a Visual Basic property,
215 /// it also initializes the Visual Basic translators if they have not been initialized
217 /// <param name="propertyInfo"></param>
218 /// <param name="propertyTranslator"></param>
219 /// <returns></returns>
220 private static bool TryGetTranslator(PropertyInfo propertyInfo, out PropertyTranslator propertyTranslator)
222 //If the type is generic, we try to match the generic property
223 PropertyInfo nonGenericPropertyInfo = propertyInfo;
224 if (propertyInfo.DeclaringType.IsGenericType)
228 propertyInfo = propertyInfo.DeclaringType.GetGenericTypeDefinition().GetProperty(propertyInfo.Name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
230 catch (AmbiguousMatchException)
232 propertyTranslator = null;
235 if (propertyInfo == null)
237 propertyTranslator = null;
242 PropertyTranslator translatorInstance;
243 if (s_propertyTranslators.TryGetValue(propertyInfo, out translatorInstance))
245 propertyTranslator = translatorInstance;
249 // check if this is the visual basic assembly
250 if (s_visualBasicAssemblyFullName == propertyInfo.DeclaringType.Assembly.FullName)
252 lock (s_vbInitializerLock)
254 if (!s_vbPropertiesInitialized)
256 InitializeVBProperties(propertyInfo.DeclaringType.Assembly);
257 s_vbPropertiesInitialized = true;
260 if (s_propertyTranslators.TryGetValue(propertyInfo, out translatorInstance))
262 propertyTranslator = translatorInstance;
267 propertyTranslator = null;
273 if (GenericICollectionTranslator.TryGetPropertyTranslator(nonGenericPropertyInfo, out propertyTranslator))
278 propertyTranslator = null;
282 // Determines if the given property can be resolved as a standard or navigation property.
283 private static bool TryResolveAsProperty(ExpressionConverter parent,
284 MemberInfo clrMember, TypeUsage definingType, DbExpression instance, out DbExpression propertyExpression)
286 // retrieve members directly from row types, which are not mapped between O and C
287 RowType rowType = definingType.EdmType as RowType;
288 string name = clrMember.Name;
293 if (rowType.Members.TryGetValue(name, false, out member))
295 propertyExpression = instance.Property(name);
299 propertyExpression = null;
303 // for non-row structural types, map from the O to the C layer using the perspective
304 StructuralType structuralType = definingType.EdmType as StructuralType;
305 if (null != structuralType)
307 EdmMember member = null;
308 if (parent._perspective.TryGetMember(structuralType, name, false, out member))
312 if (member.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty)
314 NavigationProperty navProp = (NavigationProperty)member;
315 propertyExpression = TranslateNavigationProperty(parent, clrMember, instance, navProp);
320 propertyExpression = instance.Property(name);
328 // try to unwrap GroupBy "Key" member
329 if (name == ExpressionConverter.KeyColumnName)
331 // see if we can "unwrap" the current instance
332 if (DbExpressionKind.Property == instance.ExpressionKind)
334 DbPropertyExpression property = (DbPropertyExpression)instance;
335 InitializerMetadata initializerMetadata;
337 // if we're dealing with the "Group" property of a GroupBy projection, we know how to unwrap
339 if (property.Property.Name == ExpressionConverter.GroupColumnName && // only know how to unwrap the group
340 InitializerMetadata.TryGetInitializerMetadata(property.Instance.ResultType, out initializerMetadata) &&
341 initializerMetadata.Kind == InitializerMetadataKind.Grouping)
343 propertyExpression = property.Instance.Property(ExpressionConverter.KeyColumnName);
349 propertyExpression = null;
353 private static DbExpression TranslateNavigationProperty(ExpressionConverter parent, MemberInfo clrMember, DbExpression instance, NavigationProperty navProp)
355 DbExpression propertyExpression;
356 propertyExpression = instance.Property(navProp);
358 // for EntityCollection navigations, wrap in "grouping" where the key is the parent
359 // entity and the group contains the child entities
360 // For non-EntityCollection navigations (e.g. from POCO entities), we just need the
361 // enumeration, not the grouping
362 if (BuiltInTypeKind.CollectionType == propertyExpression.ResultType.EdmType.BuiltInTypeKind)
364 Debug.Assert(clrMember is PropertyInfo, "Navigation property was not a property; should not be allowed by metadata.");
365 Type propertyType = ((PropertyInfo)clrMember).PropertyType;
366 if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(EntityCollection<>))
368 List<KeyValuePair<string, DbExpression>> collectionColumns =
369 new List<KeyValuePair<string, DbExpression>>(2);
370 collectionColumns.Add(new KeyValuePair<string, DbExpression>(
371 ExpressionConverter.EntityCollectionOwnerColumnName, instance));
372 collectionColumns.Add(new KeyValuePair<string, DbExpression>(
373 ExpressionConverter.EntityCollectionElementsColumnName, propertyExpression));
374 propertyExpression = parent.CreateNewRowExpression(collectionColumns,
375 InitializerMetadata.CreateEntityCollectionInitializer(parent.EdmItemCollection, ((PropertyInfo)clrMember).PropertyType, navProp));
378 return propertyExpression;
381 private static DbExpression TranslateCount(ExpressionConverter parent, Type sequenceElementType, Expression sequence)
383 // retranslate as a Count() aggregate, since the name collision prevents us
384 // from calling the method directly in VB and C#
385 MethodInfo countMethod;
386 ReflectionUtil.TryLookupMethod(SequenceMethod.Count, out countMethod);
387 Debug.Assert(null != countMethod, "Count() must exist");
388 countMethod = countMethod.MakeGenericMethod(sequenceElementType);
389 Expression countCall = Expression.Call(countMethod, sequence);
390 return parent.TranslateExpression(countCall);
393 private static void InitializeVBProperties(Assembly vbAssembly)
395 Debug.Assert(!s_vbPropertiesInitialized);
396 foreach (PropertyTranslator translator in GetVisualBasicPropertyTranslators(vbAssembly))
398 foreach (PropertyInfo property in translator.Properties)
400 s_propertyTranslators.Add(property, translator);
405 private static IEnumerable<PropertyTranslator> GetVisualBasicPropertyTranslators(Assembly vbAssembly)
407 yield return new VBDateAndTimeNowTranslator(vbAssembly);
410 private static IEnumerable<PropertyTranslator> GetPropertyTranslators()
412 yield return new DefaultCanonicalFunctionPropertyTranslator();
413 yield return new RenameCanonicalFunctionPropertyTranslator();
414 yield return new EntityCollectionCountTranslator();
415 yield return new NullableHasValueTranslator();
416 yield return new NullableValueTranslator();
417 yield return new SpatialPropertyTranslator();
421 /// This method is used to determine whether client side evaluation should be done,
422 /// if the property can be evaluated in the store, it is not being evaluated on the client
424 internal static bool CanFuncletizePropertyInfo(PropertyInfo propertyInfo)
426 PropertyTranslator propertyTranslator;
427 // In most cases, we only allow funcletization of properties that could not otherwise be
428 // handled by the query pipeline. ICollection<>.Count is the one exception to the rule
429 // (avoiding a breaking change)
430 return GenericICollectionTranslator.TryGetPropertyTranslator(propertyInfo, out propertyTranslator) ||
431 !TryGetTranslator(propertyInfo, out propertyTranslator);
435 #region Dynamic Property Translators
437 private sealed class GenericICollectionTranslator : PropertyTranslator
439 private readonly Type _elementType;
441 private GenericICollectionTranslator(Type elementType) : base(Enumerable.Empty<PropertyInfo>())
443 _elementType = elementType;
446 internal override DbExpression Translate(ExpressionConverter parent, MemberExpression call)
448 return TranslateCount(parent, _elementType, call.Expression);
451 internal static bool TryGetPropertyTranslator(PropertyInfo propertyInfo, out PropertyTranslator propertyTranslator)
453 // Implementation note: When adding support for additional properties, use less expensive checks
454 // such as property name and return type to test for a property defined by ICollection<T> first
455 // before calling the more expensive TypeSystem.FindICollection to test whether the declaring type
456 // of the property implements ICollection<T>.
461 if (propertyInfo.Name == "Count" &&
462 propertyInfo.PropertyType.Equals(typeof(int)))
464 foreach (KeyValuePair<Type, Type> implementedCollectionInfo in GetImplementedICollections(propertyInfo.DeclaringType))
466 Type implementedCollection = implementedCollectionInfo.Key;
467 Type elementType = implementedCollectionInfo.Value;
469 if (propertyInfo.IsImplementationOf(implementedCollection))
471 propertyTranslator = new GenericICollectionTranslator(elementType);
477 // Not a supported ICollection<T> property
478 propertyTranslator = null;
482 private static bool IsICollection(Type candidateType, out Type elementType)
484 if (candidateType.IsGenericType &&
485 candidateType.GetGenericTypeDefinition().Equals(typeof(System.Collections.Generic.ICollection<>)))
487 elementType = candidateType.GetGenericArguments()[0];
494 private static IEnumerable<KeyValuePair<Type, Type>> GetImplementedICollections(Type type)
496 Type collectionElementType;
497 if (IsICollection(type, out collectionElementType))
499 yield return new KeyValuePair<Type, Type>(type, collectionElementType);
503 foreach (Type interfaceType in type.GetInterfaces())
505 if (IsICollection(interfaceType, out collectionElementType))
507 yield return new KeyValuePair<Type, Type>(interfaceType, collectionElementType);
516 #region Signature-based Property Translators
517 private abstract class PropertyTranslator
519 private readonly IEnumerable<PropertyInfo> _properties;
520 protected PropertyTranslator(params PropertyInfo[] properties) { _properties = properties; }
521 protected PropertyTranslator(IEnumerable<PropertyInfo> properties) { _properties = properties; }
522 internal IEnumerable<PropertyInfo> Properties { get { return _properties; } }
523 internal abstract DbExpression Translate(ExpressionConverter parent, MemberExpression call);
524 public override string ToString()
526 return GetType().Name;
530 private sealed class DefaultCanonicalFunctionPropertyTranslator : PropertyTranslator
532 internal DefaultCanonicalFunctionPropertyTranslator()
533 : base(GetProperties()) { }
535 private static IEnumerable<PropertyInfo> GetProperties()
537 yield return typeof(String).GetProperty("Length", BindingFlags.Public | BindingFlags.Instance);
538 yield return typeof(DateTime).GetProperty("Year", BindingFlags.Public | BindingFlags.Instance);
539 yield return typeof(DateTime).GetProperty("Month", BindingFlags.Public | BindingFlags.Instance);
540 yield return typeof(DateTime).GetProperty("Day", BindingFlags.Public | BindingFlags.Instance);
541 yield return typeof(DateTime).GetProperty("Hour", BindingFlags.Public | BindingFlags.Instance);
542 yield return typeof(DateTime).GetProperty("Minute", BindingFlags.Public | BindingFlags.Instance);
543 yield return typeof(DateTime).GetProperty("Second", BindingFlags.Public | BindingFlags.Instance);
544 yield return typeof(DateTime).GetProperty("Millisecond", BindingFlags.Public | BindingFlags.Instance);
546 yield return typeof(DateTimeOffset).GetProperty("Year", BindingFlags.Public | BindingFlags.Instance);
547 yield return typeof(DateTimeOffset).GetProperty("Month", BindingFlags.Public | BindingFlags.Instance);
548 yield return typeof(DateTimeOffset).GetProperty("Day", BindingFlags.Public | BindingFlags.Instance);
549 yield return typeof(DateTimeOffset).GetProperty("Hour", BindingFlags.Public | BindingFlags.Instance);
550 yield return typeof(DateTimeOffset).GetProperty("Minute", BindingFlags.Public | BindingFlags.Instance);
551 yield return typeof(DateTimeOffset).GetProperty("Second", BindingFlags.Public | BindingFlags.Instance);
552 yield return typeof(DateTimeOffset).GetProperty("Millisecond", BindingFlags.Public | BindingFlags.Instance);
555 // Default translator for method calls into canonical functions.
557 // object.PropertyName -> PropertyName(object)
558 internal override DbExpression Translate(ExpressionConverter parent, MemberExpression call)
560 return parent.TranslateIntoCanonicalFunction(call.Member.Name, call, call.Expression);
564 private sealed class RenameCanonicalFunctionPropertyTranslator : PropertyTranslator
566 private static readonly Dictionary<PropertyInfo, string> s_propertyRenameMap = new Dictionary<PropertyInfo, string>(2);
568 internal RenameCanonicalFunctionPropertyTranslator()
569 : base(GetProperties()) { }
571 private static IEnumerable<PropertyInfo> GetProperties()
573 yield return GetProperty(typeof(DateTime), "Now", BindingFlags.Public | BindingFlags.Static, ExpressionConverter.CurrentDateTime);
574 yield return GetProperty(typeof(DateTime), "UtcNow", BindingFlags.Public | BindingFlags.Static, ExpressionConverter.CurrentUtcDateTime);
575 yield return GetProperty(typeof(DateTimeOffset), "Now", BindingFlags.Public | BindingFlags.Static, ExpressionConverter.CurrentDateTimeOffset);
577 yield return GetProperty(typeof(TimeSpan), "Hours", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Hour);
578 yield return GetProperty(typeof(TimeSpan), "Minutes", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Minute);
579 yield return GetProperty(typeof(TimeSpan), "Seconds", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Second);
580 yield return GetProperty(typeof(TimeSpan), "Milliseconds", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Millisecond);
583 private static PropertyInfo GetProperty(Type declaringType, string propertyName, BindingFlags bindingFlages, string canonicalFunctionName)
585 PropertyInfo propertyInfo = declaringType.GetProperty(propertyName, bindingFlages);
586 s_propertyRenameMap.Add(propertyInfo, canonicalFunctionName);
590 // Translator for static properties into canonical functions when there is a corresponding
591 // canonical function but with a differnet name
593 // object.PropertyName -> CanonicalFunctionName(object)
594 // Type.PropertyName -> CanonicalFunctionName()
595 internal override DbExpression Translate(ExpressionConverter parent, MemberExpression call)
597 PropertyInfo property = (PropertyInfo)call.Member;
598 String canonicalFunctionName = s_propertyRenameMap[property];
600 if (call.Expression == null)
602 result = parent.TranslateIntoCanonicalFunction(canonicalFunctionName, call);
606 result = parent.TranslateIntoCanonicalFunction(canonicalFunctionName, call, call.Expression);
612 private sealed class VBDateAndTimeNowTranslator : PropertyTranslator
614 private const string s_dateAndTimeTypeFullName = "Microsoft.VisualBasic.DateAndTime";
616 internal VBDateAndTimeNowTranslator(Assembly vbAssembly)
617 : base(GetProperty(vbAssembly)) { }
619 private static PropertyInfo GetProperty(Assembly vbAssembly)
621 return vbAssembly.GetType(s_dateAndTimeTypeFullName).GetProperty("Now", BindingFlags.Public | BindingFlags.Static);
626 internal override DbExpression Translate(ExpressionConverter parent, MemberExpression call)
628 return parent.TranslateIntoCanonicalFunction(ExpressionConverter.CurrentDateTime, call);
632 private sealed class EntityCollectionCountTranslator : PropertyTranslator
634 internal EntityCollectionCountTranslator()
635 : base(GetProperty()) { }
637 private static PropertyInfo GetProperty()
639 return typeof(EntityCollection<>).GetProperty(ExpressionConverter.s_entityCollectionCountPropertyName, BindingFlags.Public | BindingFlags.Instance);
643 // EntityCollection<T>.Count -> Count()
644 internal override DbExpression Translate(ExpressionConverter parent, MemberExpression call)
646 // retranslate as a Count() aggregate, since the name collision prevents us
647 // from calling the method directly in VB and C#
648 return MemberAccessTranslator.TranslateCount(parent, call.Member.DeclaringType.GetGenericArguments()[0], call.Expression);
652 private sealed class NullableHasValueTranslator : PropertyTranslator
654 internal NullableHasValueTranslator()
655 : base(GetProperty()) { }
657 private static PropertyInfo GetProperty()
659 return typeof(Nullable<>).GetProperty(ExpressionConverter.s_nullableHasValuePropertyName, BindingFlags.Public | BindingFlags.Instance);
663 // Nullable<T>.HasValue -> Not(IsNull(arg))
664 internal override DbExpression Translate(ExpressionConverter parent, MemberExpression call)
666 DbExpression argument = parent.TranslateExpression(call.Expression);
667 Debug.Assert(!TypeSemantics.IsCollectionType(argument.ResultType), "Did not expect collection type");
668 return parent.CreateIsNullExpression(argument, call.Expression.Type).Not();
672 private sealed class NullableValueTranslator : PropertyTranslator
674 internal NullableValueTranslator()
675 : base(GetProperty()) { }
677 private static PropertyInfo GetProperty()
679 return typeof(Nullable<>).GetProperty(ExpressionConverter.s_nullableValuePropertyName, BindingFlags.Public | BindingFlags.Instance);
683 // Nullable<T>.Value -> arg
684 internal override DbExpression Translate(ExpressionConverter parent, MemberExpression call)
686 DbExpression argument = parent.TranslateExpression(call.Expression);
687 Debug.Assert(!TypeSemantics.IsCollectionType(argument.ResultType), "Did not expect collection type");
693 private sealed class ParameterTranslator
694 : TypedTranslator<ParameterExpression>
696 internal ParameterTranslator()
697 : base(ExpressionType.Parameter) { }
698 protected override DbExpression TypedTranslate(ExpressionConverter parent, ParameterExpression linq)
700 // Bindings should be intercepted before we get to this point (in ExpressionConverter.TranslateExpression)
701 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ELinq_UnboundParameterExpression(linq.Name));
704 private sealed class NewTranslator
705 : TypedTranslator<NewExpression>
707 internal NewTranslator()
708 : base(ExpressionType.New) { }
709 protected override DbExpression TypedTranslate(ExpressionConverter parent, NewExpression linq)
711 int memberCount = null == linq.Members ? 0 : linq.Members.Count;
713 if (null == linq.Constructor ||
714 linq.Arguments.Count != memberCount)
716 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedConstructor);
719 parent.CheckInitializerType(linq.Type);
721 List<KeyValuePair<String, DbExpression>> recordColumns =
722 new List<KeyValuePair<string, DbExpression>>(memberCount + 1);
724 HashSet<string> memberNames = new HashSet<string>(StringComparer.Ordinal);
725 for (int i = 0; i < memberCount; i++)
729 MemberInfo memberInfo = TypeSystem.PropertyOrField(linq.Members[i], out memberName, out memberType);
730 DbExpression memberValue = parent.TranslateExpression(linq.Arguments[i]);
731 memberNames.Add(memberName);
732 recordColumns.Add(new KeyValuePair<string, DbExpression>(memberName, memberValue));
735 InitializerMetadata initializerMetadata;
736 if (0 == memberCount)
738 // add a sentinel column because CQTs do not accept empty row types
739 recordColumns.Add(DbExpressionBuilder.True.As(KeyColumnName));
740 initializerMetadata = InitializerMetadata.CreateEmptyProjectionInitializer(parent.EdmItemCollection, linq);
744 // Construct a new initializer type in metadata for this projection (provides the
745 // necessary context for the object materializer)
746 initializerMetadata = InitializerMetadata.CreateProjectionInitializer(parent.EdmItemCollection, linq);
748 parent.ValidateInitializerMetadata(initializerMetadata);
750 DbNewInstanceExpression projection = parent.CreateNewRowExpression(recordColumns, initializerMetadata);
755 private sealed class NewArrayInitTranslator
756 : TypedTranslator<NewArrayExpression>
758 internal NewArrayInitTranslator()
759 : base(ExpressionType.NewArrayInit) { }
760 protected override DbExpression TypedTranslate(ExpressionConverter parent, NewArrayExpression linq)
762 if (linq.Expressions.Count > 0)
764 return DbExpressionBuilder.NewCollection(linq.Expressions.Select(e => parent.TranslateExpression(e)));
768 if (typeof(byte[]) == linq.Type)
771 if (parent.TryGetValueLayerType(typeof(byte), out type))
773 typeUsage = TypeHelpers.CreateCollectionTypeUsage(type);
774 return DbExpressionBuilder.NewEmptyCollection(typeUsage);
779 if (parent.TryGetValueLayerType(linq.Type, out typeUsage))
781 return DbExpressionBuilder.NewEmptyCollection(typeUsage);
785 throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedType(DescribeClrType(linq.Type)));
788 private sealed class ListInitTranslator
789 : TypedTranslator<ListInitExpression>
791 internal ListInitTranslator()
792 : base(ExpressionType.ListInit ) { }
793 protected override DbExpression TypedTranslate(ExpressionConverter parent, ListInitExpression linq)
795 // Ensure requirements: one list initializer argument and a default constructor.
796 if ((linq.NewExpression.Constructor != null) && (linq.NewExpression.Constructor.GetParameters().Length != 0))
798 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedConstructor);
801 if (linq.Initializers.Any(i => i.Arguments.Count != 1))
803 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedInitializers);
806 return DbExpressionBuilder.NewCollection(linq.Initializers.Select(i => parent.TranslateExpression(i.Arguments[0])));
809 private sealed class MemberInitTranslator
810 : TypedTranslator<MemberInitExpression>
812 internal MemberInitTranslator()
813 : base(ExpressionType.MemberInit) { }
814 protected override DbExpression TypedTranslate(ExpressionConverter parent, MemberInitExpression linq)
816 if (null == linq.NewExpression.Constructor ||
817 0 != linq.NewExpression.Constructor.GetParameters().Length)
819 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedConstructor);
822 parent.CheckInitializerType(linq.Type);
824 List<KeyValuePair<String, DbExpression>> recordColumns =
825 new List<KeyValuePair<string, DbExpression>>(linq.Bindings.Count + 1);
826 MemberInfo[] members = new MemberInfo[linq.Bindings.Count];
828 HashSet<string> memberNames = new HashSet<string>(StringComparer.Ordinal);
829 for (int i = 0; i < linq.Bindings.Count; i++)
831 MemberAssignment binding = linq.Bindings[i] as MemberAssignment;
834 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedBinding);
838 MemberInfo memberInfo = TypeSystem.PropertyOrField(binding.Member, out memberName, out memberType);
840 DbExpression memberValue = parent.TranslateExpression(binding.Expression);
841 memberNames.Add(memberName);
842 members[i] = memberInfo;
843 recordColumns.Add(new KeyValuePair<string, DbExpression>(memberName, memberValue));
846 InitializerMetadata initializerMetadata;
848 if (0 == recordColumns.Count)
850 // add a sentinel column because CQTs do not accept empty row types
851 recordColumns.Add(DbExpressionBuilder.Constant(true).As(KeyColumnName));
852 initializerMetadata = InitializerMetadata.CreateEmptyProjectionInitializer(parent.EdmItemCollection, linq.NewExpression);
856 // Construct a new initializer type in metadata for this projection (provides the
857 // necessary context for the object materializer)
858 initializerMetadata = InitializerMetadata.CreateProjectionInitializer(parent.EdmItemCollection, linq, members);
860 parent.ValidateInitializerMetadata(initializerMetadata);
861 DbNewInstanceExpression projection = parent.CreateNewRowExpression(recordColumns, initializerMetadata);
866 private sealed class ConditionalTranslator : TypedTranslator<ConditionalExpression>
868 internal ConditionalTranslator()
869 : base(ExpressionType.Conditional) { }
870 protected override DbExpression TypedTranslate(ExpressionConverter parent, ConditionalExpression linq)
872 // translate Test ? IfTrue : IfFalse --> CASE WHEN Test THEN IfTrue ELSE IfFalse
873 List<DbExpression> whenExpressions = new List<DbExpression>(1);
874 whenExpressions.Add(parent.TranslateExpression(linq.Test));
875 List<DbExpression> thenExpressions = new List<DbExpression>(1);
876 thenExpressions.Add(parent.TranslateExpression(linq.IfTrue));
877 DbExpression elseExpression = parent.TranslateExpression(linq.IfFalse);
878 return DbExpressionBuilder.Case(whenExpressions, thenExpressions, elseExpression);
881 private sealed class NotSupportedTranslator : Translator
883 internal NotSupportedTranslator(params ExpressionType[] nodeTypes)
887 internal override DbExpression Translate(ExpressionConverter parent, Expression linq)
889 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedExpressionType(linq.NodeType));
892 private sealed class ExtensionTranslator : Translator
894 internal ExtensionTranslator()
895 : base(EntityExpressionVisitor.CustomExpression)
898 internal override DbExpression Translate(ExpressionConverter parent, Expression linq)
900 QueryParameterExpression queryParameter = linq as QueryParameterExpression;
901 if (null == queryParameter)
903 throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedExpressionType(linq.NodeType));
905 // otherwise add a new query parameter...
906 parent.AddParameter(queryParameter);
907 return queryParameter.ParameterReference;
912 #region Binary expression translators
913 private abstract class BinaryTranslator
914 : TypedTranslator<System.Linq.Expressions.BinaryExpression>
916 protected BinaryTranslator(params ExpressionType[] nodeTypes)
917 : base(nodeTypes) { }
918 protected override DbExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq)
920 return TranslateBinary(parent, parent.TranslateExpression(linq.Left), parent.TranslateExpression(linq.Right), linq);
922 protected abstract DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq);
924 private sealed class CoalesceTranslator : BinaryTranslator
926 internal CoalesceTranslator()
927 : base(ExpressionType.Coalesce) { }
928 protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq)
930 // left ?? right gets translated to:
931 // CASE WHEN IsNull(left) THEN right ELSE left
934 DbExpression isNull = parent.CreateIsNullExpression(left, linq.Left.Type);
936 // construct case expression
937 List<DbExpression> whenExpressions = new List<DbExpression>(1);
938 whenExpressions.Add(isNull);
939 List<DbExpression> thenExpressions = new List<DbExpression>(1);
940 thenExpressions.Add(right);
941 DbExpression caseExpression = DbExpressionBuilder.Case(whenExpressions,
942 thenExpressions, left);
944 return caseExpression;
947 private sealed class AndAlsoTranslator : BinaryTranslator
949 internal AndAlsoTranslator()
950 : base(ExpressionType.AndAlso) { }
951 protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq)
953 return left.And(right);
956 private sealed class OrElseTranslator : BinaryTranslator
958 internal OrElseTranslator()
959 : base(ExpressionType.OrElse) { }
960 protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq)
962 return left.Or(right);
965 private sealed class LessThanTranslator : BinaryTranslator
967 internal LessThanTranslator()
968 : base(ExpressionType.LessThan) { }
969 protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq)
971 return left.LessThan(right);
974 private sealed class LessThanOrEqualsTranslator : BinaryTranslator
976 internal LessThanOrEqualsTranslator()
977 : base(ExpressionType.LessThanOrEqual) { }
978 protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq)
980 return left.LessThanOrEqual(right);
983 private sealed class GreaterThanTranslator : BinaryTranslator
985 internal GreaterThanTranslator()
986 : base(ExpressionType.GreaterThan) { }
987 protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq)
989 return left.GreaterThan(right);
992 private sealed class GreaterThanOrEqualsTranslator : BinaryTranslator
994 internal GreaterThanOrEqualsTranslator()
995 : base(ExpressionType.GreaterThanOrEqual) { }
996 protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq)
998 return left.GreaterThanOrEqual(right);
1001 private sealed class EqualsTranslator : TypedTranslator<System.Linq.Expressions.BinaryExpression>
1003 internal EqualsTranslator()
1004 : base(ExpressionType.Equal) { }
1005 protected override DbExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq)
1007 Expression linqLeft = linq.Left;
1008 Expression linqRight = linq.Right;
1010 bool leftIsNull = ExpressionIsNullConstant(linqLeft);
1011 bool rightIsNull = ExpressionIsNullConstant(linqRight);
1013 // if both values are null, short-circuit
1014 if (leftIsNull && rightIsNull) { return DbExpressionBuilder.True; }
1016 // if only one side is null, produce an IsNull statement
1017 if (leftIsNull) { return CreateIsNullExpression(parent, linqRight); }
1018 if (rightIsNull) { return CreateIsNullExpression(parent, linqLeft); }
1020 // create a standard equals expression, calling utility method to compensate for null equality
1021 DbExpression cqtLeft = parent.TranslateExpression(linqLeft);
1022 DbExpression cqtRight = parent.TranslateExpression(linqRight);
1023 EqualsPattern pattern = EqualsPattern.Store;
1024 if (parent._funcletizer.RootContext.ContextOptions.UseCSharpNullComparisonBehavior)
1026 pattern = EqualsPattern.PositiveNullEqualityComposable;
1028 return parent.CreateEqualsExpression(cqtLeft, cqtRight, pattern, linqLeft.Type, linqRight.Type);
1030 private static DbExpression CreateIsNullExpression(ExpressionConverter parent, Expression input)
1032 input = UnwrapConvert(input);
1035 DbExpression inputCqt = parent.TranslateExpression(input);
1037 // create IsNull expression
1038 return parent.CreateIsNullExpression(inputCqt, input.Type);
1040 private static bool ExpressionIsNullConstant(Expression expression)
1042 // convert statements introduced by compiler should not affect nullness
1043 expression = UnwrapConvert(expression);
1045 // check if the unwrapped expression is a null constant
1046 if (ExpressionType.Constant != expression.NodeType) { return false; }
1047 System.Linq.Expressions.ConstantExpression constant = (System.Linq.Expressions.ConstantExpression)expression;
1048 return null == constant.Value;
1050 private static Expression UnwrapConvert(Expression input)
1052 // unwrap all converts
1053 while (ExpressionType.Convert == input.NodeType)
1055 input = ((UnaryExpression)input).Operand;
1060 private sealed class NotEqualsTranslator : TypedTranslator<System.Linq.Expressions.BinaryExpression>
1062 internal NotEqualsTranslator()
1063 : base(ExpressionType.NotEqual) { }
1064 protected override DbExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq)
1066 // rewrite as a not equals expression
1067 Expression notLinq = Expression.Not(
1068 Expression.Equal(linq.Left, linq.Right));
1069 return parent.TranslateExpression(notLinq);
1074 #region Type binary expression translator
1075 private sealed class IsTranslator : TypedTranslator<TypeBinaryExpression>
1077 internal IsTranslator()
1078 : base(ExpressionType.TypeIs) { }
1079 protected override DbExpression TypedTranslate(ExpressionConverter parent, TypeBinaryExpression linq)
1081 DbExpression operand = parent.TranslateExpression(linq.Expression);
1082 TypeUsage fromType = operand.ResultType;
1083 TypeUsage toType = parent.GetIsOrAsTargetType(fromType, ExpressionType.TypeIs, linq.TypeOperand, linq.Expression.Type);
1084 return operand.IsOf(toType);
1089 #region Arithmetic expressions
1090 private sealed class AddTranslator : BinaryTranslator
1092 internal AddTranslator()
1093 : base(ExpressionType.Add, ExpressionType.AddChecked) { }
1094 protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq)
1096 if (TypeSemantics.IsPrimitiveType(left.ResultType, PrimitiveTypeKind.String) &&
1097 TypeSemantics.IsPrimitiveType(right.ResultType, PrimitiveTypeKind.String))
1099 // Add(string, string) => Concat(string, string)
1100 return parent.CreateCanonicalFunction(ExpressionConverter.Concat, linq, left, right);
1104 return left.Plus(right);
1108 private sealed class DivideTranslator : BinaryTranslator
1110 internal DivideTranslator()
1111 : base(ExpressionType.Divide) { }
1112 protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq)
1114 return left.Divide(right);
1117 private sealed class ModuloTranslator : BinaryTranslator
1119 internal ModuloTranslator()
1120 : base(ExpressionType.Modulo) { }
1121 protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq)
1123 return left.Modulo(right);
1126 private sealed class MultiplyTranslator : BinaryTranslator
1128 internal MultiplyTranslator()
1129 : base(ExpressionType.Multiply, ExpressionType.MultiplyChecked) { }
1130 protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq)
1132 return left.Multiply(right);
1135 private sealed class SubtractTranslator : BinaryTranslator
1137 internal SubtractTranslator()
1138 : base(ExpressionType.Subtract, ExpressionType.SubtractChecked) { }
1139 protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq)
1141 return left.Minus(right);
1144 private sealed class NegateTranslator : UnaryTranslator
1146 internal NegateTranslator()
1147 : base(ExpressionType.Negate, ExpressionType.NegateChecked) { }
1148 protected override DbExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, DbExpression operand)
1150 return operand.UnaryMinus();
1153 private sealed class UnaryPlusTranslator : UnaryTranslator
1155 internal UnaryPlusTranslator()
1156 : base(ExpressionType.UnaryPlus) { }
1157 protected override DbExpression TranslateUnary(ExpressionConverter parent, UnaryExpression unary, DbExpression operand)
1165 #region Bitwise expressions
1166 private abstract class BitwiseBinaryTranslator : TypedTranslator<System.Linq.Expressions.BinaryExpression>
1168 private readonly string _canonicalFunctionName;
1170 protected BitwiseBinaryTranslator(ExpressionType nodeType, string canonicalFunctionName)
1173 _canonicalFunctionName = canonicalFunctionName;
1176 protected override DbExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq)
1178 DbExpression left = parent.TranslateExpression(linq.Left);
1179 DbExpression right = parent.TranslateExpression(linq.Right);
1181 //If the arguments are binary we translate into logic expressions
1182 if (TypeSemantics.IsBooleanType(left.ResultType))
1184 return TranslateIntoLogicExpression(parent, linq, left, right);
1187 //Otherwise we translate into bitwise canonical functions
1188 return parent.CreateCanonicalFunction(_canonicalFunctionName, linq, left, right);
1190 protected abstract DbExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, DbExpression left, DbExpression right);
1192 private sealed class AndTranslator : BitwiseBinaryTranslator
1194 internal AndTranslator()
1195 : base(ExpressionType.And, ExpressionConverter.BitwiseAnd) { }
1196 protected override DbExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, DbExpression left, DbExpression right)
1198 return left.And(right);
1201 private sealed class OrTranslator : BitwiseBinaryTranslator
1203 internal OrTranslator()
1204 : base(ExpressionType.Or, ExpressionConverter.BitwiseOr) { }
1205 protected override DbExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, DbExpression left, DbExpression right)
1207 return left.Or(right);
1210 private sealed class ExclusiveOrTranslator : BitwiseBinaryTranslator
1212 internal ExclusiveOrTranslator()
1213 : base(ExpressionType.ExclusiveOr, ExpressionConverter.BitwiseXor) { }
1214 protected override DbExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, DbExpression left, DbExpression right)
1216 //No direct translation, we translate into ((left && !right) || (!left && right))
1217 DbExpression firstExpression = left.And(right.Not());
1218 DbExpression secondExpression = left.Not().And(right);
1219 DbExpression result = firstExpression.Or(secondExpression);
1223 private sealed class NotTranslator : TypedTranslator<UnaryExpression>
1225 internal NotTranslator()
1226 : base(ExpressionType.Not) { }
1227 protected override DbExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression linq)
1229 DbExpression operand = parent.TranslateExpression(linq.Operand);
1230 if (TypeSemantics.IsBooleanType(operand.ResultType))
1232 return operand.Not();
1234 return parent.CreateCanonicalFunction(ExpressionConverter.BitwiseNot, linq, operand);
1239 #region Unary expression translators
1240 private abstract class UnaryTranslator
1241 : TypedTranslator<System.Linq.Expressions.UnaryExpression>
1243 protected UnaryTranslator(params ExpressionType[] nodeTypes)
1244 : base(nodeTypes) { }
1245 protected override DbExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression linq)
1247 return TranslateUnary(parent, linq, parent.TranslateExpression(linq.Operand));
1249 protected abstract DbExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, DbExpression operand);
1251 private sealed class QuoteTranslator : UnaryTranslator
1253 internal QuoteTranslator()
1254 : base(ExpressionType.Quote) { }
1255 protected override DbExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, DbExpression operand)
1257 // simply return the operand: expressions compilations not cached for LINQ, so
1258 // parameters are always bound properly
1262 private sealed class ConvertTranslator : UnaryTranslator
1264 internal ConvertTranslator()
1265 : base(ExpressionType.Convert, ExpressionType.ConvertChecked) { }
1266 protected override DbExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, DbExpression operand)
1268 Type toClrType = unary.Type;
1269 Type fromClrType = unary.Operand.Type;
1270 DbExpression cast = parent.CreateCastExpression(operand, toClrType, fromClrType);
1274 private sealed class AsTranslator : UnaryTranslator
1276 internal AsTranslator()
1277 : base(ExpressionType.TypeAs) { }
1278 protected override DbExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, DbExpression operand)
1280 TypeUsage fromType = operand.ResultType;
1281 TypeUsage toType = parent.GetIsOrAsTargetType(fromType, ExpressionType.TypeAs, unary.Type, unary.Operand.Type);
1282 return operand.TreatAs(toType);