//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....], [....] //--------------------------------------------------------------------- namespace System.Data.Objects.ELinq { using System.Collections.Generic; using System.Data.Common; using System.Data.Common.CommandTrees; using System.Data.Common.CommandTrees.ExpressionBuilder; using System.Data.Entity; using System.Data.Metadata.Edm; using System.Data.Objects.DataClasses; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Reflection; internal sealed partial class ExpressionConverter { // Base class supporting the translation of LINQ node type(s) given a LINQ expression // of that type, and the "parent" translation context (the ExpressionConverter processor) private abstract class Translator { private readonly ExpressionType[] _nodeTypes; protected Translator(params ExpressionType[] nodeTypes) { _nodeTypes = nodeTypes; } // Gets LINQ node types this translator should be registed to process. internal IEnumerable NodeTypes { get { return _nodeTypes; } } internal abstract DbExpression Translate(ExpressionConverter parent, Expression linq); public override string ToString() { return this.GetType().Name; } } #region Misc // Typed version of Translator private abstract class TypedTranslator : Translator where T_Linq : Expression { protected TypedTranslator(params ExpressionType[] nodeTypes) : base(nodeTypes) { } internal override DbExpression Translate(ExpressionConverter parent, Expression linq) { return TypedTranslate(parent, (T_Linq)linq); } protected abstract DbExpression TypedTranslate(ExpressionConverter parent, T_Linq linq); } private sealed class ConstantTranslator : TypedTranslator { internal ConstantTranslator() : base(ExpressionType.Constant) { } protected override DbExpression TypedTranslate(ExpressionConverter parent, ConstantExpression linq) { // Check to see if this constant corresponds to the compiled query context parameter (it // gets turned into a constant during funcletization and has special error handling). if (linq == parent._funcletizer.RootContextExpression) { throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ELinq_UnsupportedUseOfContextParameter( parent._funcletizer.RootContextParameter.Name)); } ObjectQuery queryOfT = linq.Value as ObjectQuery; if (null != queryOfT) { return parent.TranslateInlineQueryOfT(queryOfT); } // If it is something we can enumerate then we can evaluate locally and send to the server var values = linq.Value as System.Collections.IEnumerable; if (values != null) { Type elementType = TypeSystem.GetElementType(linq.Type); if ((elementType != null) && (elementType != linq.Type)) { var expressions = new List(); foreach (object o in values) { expressions.Add(Expression.Constant(o, elementType)); } // Invalidate the query plan every time the query is executed since it is possible // to modify an element of a collection without changing the reference. parent._recompileRequired = () => true; return parent.TranslateExpression(Expression.NewArrayInit(elementType, expressions)); } } bool isNullValue = null == linq.Value; // Remove facet information: null instances do not constrain type facets (e.g. a null string does not restrict // "length" in compatibility checks) TypeUsage type; bool typeSupported = false; if (parent.TryGetValueLayerType(linq.Type, out type)) { // For constant values, support only primitive and enum type (this is all that is supported by CQTs) // For null types, also allow EntityType. Although other types claim to be supported, they // don't work (e.g. complex type, see SQL BU 543956) if (Helper.IsScalarType(type.EdmType) || (isNullValue && Helper.IsEntityType(type.EdmType))) { typeSupported = true; } } if (!typeSupported) { if (isNullValue) { throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedNullConstant(DescribeClrType(linq.Type))); } else { throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedConstant(DescribeClrType(linq.Type))); } } // create a constant or null expression depending on value if (isNullValue) { return DbExpressionBuilder.Null(type); } else { // By default use the value specified in the ConstantExpression.Value property. However, // if the value was of an enum type that is not in the model its type was converted // to the EdmType type corresponding to the underlying type of the enum type. In this case // we also need to cast the value to the same type to avoid mismatches. var value = linq.Value; if (Helper.IsPrimitiveType(type.EdmType)) { var nonNullableLinqType = TypeSystem.GetNonNullableType(linq.Type); if (nonNullableLinqType.IsEnum) { value = System.Convert.ChangeType(linq.Value, nonNullableLinqType.GetEnumUnderlyingType(), CultureInfo.InvariantCulture); } } return DbExpressionBuilder.Constant(type, value); } } } private sealed partial class MemberAccessTranslator : TypedTranslator { internal MemberAccessTranslator() : base(ExpressionType.MemberAccess) { } // attempt to translate the member access to a "regular" property, a navigation property, or a calculated // property protected override DbExpression TypedTranslate(ExpressionConverter parent, MemberExpression linq) { DbExpression propertyExpression; string memberName; Type memberType; MemberInfo memberInfo = TypeSystem.PropertyOrField(linq.Member, out memberName, out memberType); // note: we check for "regular" properties last, since the other two flavors derive // from this one if (linq.Expression != null) { DbExpression instance = parent.TranslateExpression(linq.Expression); if (TryResolveAsProperty(parent, memberInfo, instance.ResultType, instance, out propertyExpression)) { return propertyExpression; } } if (memberInfo.MemberType == MemberTypes.Property) { // Check whether it is one of the special properties that we know how to translate PropertyTranslator propertyTranslator; if (TryGetTranslator((PropertyInfo)memberInfo, out propertyTranslator)) { return propertyTranslator.Translate(parent, linq); } } // no other property types are supported by LINQ over entities throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnrecognizedMember(linq.Member.Name)); } #region Static members and initializers private static readonly Dictionary s_propertyTranslators; private static bool s_vbPropertiesInitialized; private static readonly object s_vbInitializerLock = new object(); static MemberAccessTranslator() { // initialize translators for specific properties s_propertyTranslators = new Dictionary(); foreach (PropertyTranslator translator in GetPropertyTranslators()) { foreach (PropertyInfo property in translator.Properties) { s_propertyTranslators.Add(property, translator); } } } /// /// Tries to get a translator for the given property info. /// If the given property info corresponds to a Visual Basic property, /// it also initializes the Visual Basic translators if they have not been initialized /// /// /// /// private static bool TryGetTranslator(PropertyInfo propertyInfo, out PropertyTranslator propertyTranslator) { //If the type is generic, we try to match the generic property PropertyInfo nonGenericPropertyInfo = propertyInfo; if (propertyInfo.DeclaringType.IsGenericType) { try { propertyInfo = propertyInfo.DeclaringType.GetGenericTypeDefinition().GetProperty(propertyInfo.Name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); } catch (AmbiguousMatchException) { propertyTranslator = null; return false; } if (propertyInfo == null) { propertyTranslator = null; return false; } } PropertyTranslator translatorInstance; if (s_propertyTranslators.TryGetValue(propertyInfo, out translatorInstance)) { propertyTranslator = translatorInstance; return true; } // check if this is the visual basic assembly if (s_visualBasicAssemblyFullName == propertyInfo.DeclaringType.Assembly.FullName) { lock (s_vbInitializerLock) { if (!s_vbPropertiesInitialized) { InitializeVBProperties(propertyInfo.DeclaringType.Assembly); s_vbPropertiesInitialized = true; } // try again if (s_propertyTranslators.TryGetValue(propertyInfo, out translatorInstance)) { propertyTranslator = translatorInstance; return true; } else { propertyTranslator = null; return false; } } } if (GenericICollectionTranslator.TryGetPropertyTranslator(nonGenericPropertyInfo, out propertyTranslator)) { return true; } propertyTranslator = null; return false; } // Determines if the given property can be resolved as a standard or navigation property. private static bool TryResolveAsProperty(ExpressionConverter parent, MemberInfo clrMember, TypeUsage definingType, DbExpression instance, out DbExpression propertyExpression) { // retrieve members directly from row types, which are not mapped between O and C RowType rowType = definingType.EdmType as RowType; string name = clrMember.Name; if (null != rowType) { EdmMember member; if (rowType.Members.TryGetValue(name, false, out member)) { propertyExpression = instance.Property(name); return true; } propertyExpression = null; return false; } // for non-row structural types, map from the O to the C layer using the perspective StructuralType structuralType = definingType.EdmType as StructuralType; if (null != structuralType) { EdmMember member = null; if (parent._perspective.TryGetMember(structuralType, name, false, out member)) { if (null != member) { if (member.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty) { NavigationProperty navProp = (NavigationProperty)member; propertyExpression = TranslateNavigationProperty(parent, clrMember, instance, navProp); return true; } else { propertyExpression = instance.Property(name); return true; } } } } // try to unwrap GroupBy "Key" member if (name == ExpressionConverter.KeyColumnName) { // see if we can "unwrap" the current instance if (DbExpressionKind.Property == instance.ExpressionKind) { DbPropertyExpression property = (DbPropertyExpression)instance; InitializerMetadata initializerMetadata; // if we're dealing with the "Group" property of a GroupBy projection, we know how to unwrap // it if (property.Property.Name == ExpressionConverter.GroupColumnName && // only know how to unwrap the group InitializerMetadata.TryGetInitializerMetadata(property.Instance.ResultType, out initializerMetadata) && initializerMetadata.Kind == InitializerMetadataKind.Grouping) { propertyExpression = property.Instance.Property(ExpressionConverter.KeyColumnName); return true; } } } propertyExpression = null; return false; } private static DbExpression TranslateNavigationProperty(ExpressionConverter parent, MemberInfo clrMember, DbExpression instance, NavigationProperty navProp) { DbExpression propertyExpression; propertyExpression = instance.Property(navProp); // for EntityCollection navigations, wrap in "grouping" where the key is the parent // entity and the group contains the child entities // For non-EntityCollection navigations (e.g. from POCO entities), we just need the // enumeration, not the grouping if (BuiltInTypeKind.CollectionType == propertyExpression.ResultType.EdmType.BuiltInTypeKind) { Debug.Assert(clrMember is PropertyInfo, "Navigation property was not a property; should not be allowed by metadata."); Type propertyType = ((PropertyInfo)clrMember).PropertyType; if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(EntityCollection<>)) { List> collectionColumns = new List>(2); collectionColumns.Add(new KeyValuePair( ExpressionConverter.EntityCollectionOwnerColumnName, instance)); collectionColumns.Add(new KeyValuePair( ExpressionConverter.EntityCollectionElementsColumnName, propertyExpression)); propertyExpression = parent.CreateNewRowExpression(collectionColumns, InitializerMetadata.CreateEntityCollectionInitializer(parent.EdmItemCollection, ((PropertyInfo)clrMember).PropertyType, navProp)); } } return propertyExpression; } private static DbExpression TranslateCount(ExpressionConverter parent, Type sequenceElementType, Expression sequence) { // retranslate as a Count() aggregate, since the name collision prevents us // from calling the method directly in VB and C# MethodInfo countMethod; ReflectionUtil.TryLookupMethod(SequenceMethod.Count, out countMethod); Debug.Assert(null != countMethod, "Count() must exist"); countMethod = countMethod.MakeGenericMethod(sequenceElementType); Expression countCall = Expression.Call(countMethod, sequence); return parent.TranslateExpression(countCall); } private static void InitializeVBProperties(Assembly vbAssembly) { Debug.Assert(!s_vbPropertiesInitialized); foreach (PropertyTranslator translator in GetVisualBasicPropertyTranslators(vbAssembly)) { foreach (PropertyInfo property in translator.Properties) { s_propertyTranslators.Add(property, translator); } } } private static IEnumerable GetVisualBasicPropertyTranslators(Assembly vbAssembly) { yield return new VBDateAndTimeNowTranslator(vbAssembly); } private static IEnumerable GetPropertyTranslators() { yield return new DefaultCanonicalFunctionPropertyTranslator(); yield return new RenameCanonicalFunctionPropertyTranslator(); yield return new EntityCollectionCountTranslator(); yield return new NullableHasValueTranslator(); yield return new NullableValueTranslator(); yield return new SpatialPropertyTranslator(); } /// /// This method is used to determine whether client side evaluation should be done, /// if the property can be evaluated in the store, it is not being evaluated on the client /// internal static bool CanFuncletizePropertyInfo(PropertyInfo propertyInfo) { PropertyTranslator propertyTranslator; // In most cases, we only allow funcletization of properties that could not otherwise be // handled by the query pipeline. ICollection<>.Count is the one exception to the rule // (avoiding a breaking change) return GenericICollectionTranslator.TryGetPropertyTranslator(propertyInfo, out propertyTranslator) || !TryGetTranslator(propertyInfo, out propertyTranslator); } #endregion #region Dynamic Property Translators private sealed class GenericICollectionTranslator : PropertyTranslator { private readonly Type _elementType; private GenericICollectionTranslator(Type elementType) : base(Enumerable.Empty()) { _elementType = elementType; } internal override DbExpression Translate(ExpressionConverter parent, MemberExpression call) { return TranslateCount(parent, _elementType, call.Expression); } internal static bool TryGetPropertyTranslator(PropertyInfo propertyInfo, out PropertyTranslator propertyTranslator) { // Implementation note: When adding support for additional properties, use less expensive checks // such as property name and return type to test for a property defined by ICollection first // before calling the more expensive TypeSystem.FindICollection to test whether the declaring type // of the property implements ICollection. // // Int32 Count // if (propertyInfo.Name == "Count" && propertyInfo.PropertyType.Equals(typeof(int))) { foreach (KeyValuePair implementedCollectionInfo in GetImplementedICollections(propertyInfo.DeclaringType)) { Type implementedCollection = implementedCollectionInfo.Key; Type elementType = implementedCollectionInfo.Value; if (propertyInfo.IsImplementationOf(implementedCollection)) { propertyTranslator = new GenericICollectionTranslator(elementType); return true; } } } // Not a supported ICollection property propertyTranslator = null; return false; } private static bool IsICollection(Type candidateType, out Type elementType) { if (candidateType.IsGenericType && candidateType.GetGenericTypeDefinition().Equals(typeof(System.Collections.Generic.ICollection<>))) { elementType = candidateType.GetGenericArguments()[0]; return true; } elementType = null; return false; } private static IEnumerable> GetImplementedICollections(Type type) { Type collectionElementType; if (IsICollection(type, out collectionElementType)) { yield return new KeyValuePair(type, collectionElementType); } else { foreach (Type interfaceType in type.GetInterfaces()) { if (IsICollection(interfaceType, out collectionElementType)) { yield return new KeyValuePair(interfaceType, collectionElementType); } } } } } #endregion #region Signature-based Property Translators private abstract class PropertyTranslator { private readonly IEnumerable _properties; protected PropertyTranslator(params PropertyInfo[] properties) { _properties = properties; } protected PropertyTranslator(IEnumerable properties) { _properties = properties; } internal IEnumerable Properties { get { return _properties; } } internal abstract DbExpression Translate(ExpressionConverter parent, MemberExpression call); public override string ToString() { return GetType().Name; } } private sealed class DefaultCanonicalFunctionPropertyTranslator : PropertyTranslator { internal DefaultCanonicalFunctionPropertyTranslator() : base(GetProperties()) { } private static IEnumerable GetProperties() { yield return typeof(String).GetProperty("Length", BindingFlags.Public | BindingFlags.Instance); yield return typeof(DateTime).GetProperty("Year", BindingFlags.Public | BindingFlags.Instance); yield return typeof(DateTime).GetProperty("Month", BindingFlags.Public | BindingFlags.Instance); yield return typeof(DateTime).GetProperty("Day", BindingFlags.Public | BindingFlags.Instance); yield return typeof(DateTime).GetProperty("Hour", BindingFlags.Public | BindingFlags.Instance); yield return typeof(DateTime).GetProperty("Minute", BindingFlags.Public | BindingFlags.Instance); yield return typeof(DateTime).GetProperty("Second", BindingFlags.Public | BindingFlags.Instance); yield return typeof(DateTime).GetProperty("Millisecond", BindingFlags.Public | BindingFlags.Instance); yield return typeof(DateTimeOffset).GetProperty("Year", BindingFlags.Public | BindingFlags.Instance); yield return typeof(DateTimeOffset).GetProperty("Month", BindingFlags.Public | BindingFlags.Instance); yield return typeof(DateTimeOffset).GetProperty("Day", BindingFlags.Public | BindingFlags.Instance); yield return typeof(DateTimeOffset).GetProperty("Hour", BindingFlags.Public | BindingFlags.Instance); yield return typeof(DateTimeOffset).GetProperty("Minute", BindingFlags.Public | BindingFlags.Instance); yield return typeof(DateTimeOffset).GetProperty("Second", BindingFlags.Public | BindingFlags.Instance); yield return typeof(DateTimeOffset).GetProperty("Millisecond", BindingFlags.Public | BindingFlags.Instance); } // Default translator for method calls into canonical functions. // Translation: // object.PropertyName -> PropertyName(object) internal override DbExpression Translate(ExpressionConverter parent, MemberExpression call) { return parent.TranslateIntoCanonicalFunction(call.Member.Name, call, call.Expression); } } private sealed class RenameCanonicalFunctionPropertyTranslator : PropertyTranslator { private static readonly Dictionary s_propertyRenameMap = new Dictionary(2); internal RenameCanonicalFunctionPropertyTranslator() : base(GetProperties()) { } private static IEnumerable GetProperties() { yield return GetProperty(typeof(DateTime), "Now", BindingFlags.Public | BindingFlags.Static, ExpressionConverter.CurrentDateTime); yield return GetProperty(typeof(DateTime), "UtcNow", BindingFlags.Public | BindingFlags.Static, ExpressionConverter.CurrentUtcDateTime); yield return GetProperty(typeof(DateTimeOffset), "Now", BindingFlags.Public | BindingFlags.Static, ExpressionConverter.CurrentDateTimeOffset); yield return GetProperty(typeof(TimeSpan), "Hours", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Hour); yield return GetProperty(typeof(TimeSpan), "Minutes", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Minute); yield return GetProperty(typeof(TimeSpan), "Seconds", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Second); yield return GetProperty(typeof(TimeSpan), "Milliseconds", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Millisecond); } private static PropertyInfo GetProperty(Type declaringType, string propertyName, BindingFlags bindingFlages, string canonicalFunctionName) { PropertyInfo propertyInfo = declaringType.GetProperty(propertyName, bindingFlages); s_propertyRenameMap.Add(propertyInfo, canonicalFunctionName); return propertyInfo; } // Translator for static properties into canonical functions when there is a corresponding // canonical function but with a differnet name // Translation: // object.PropertyName -> CanonicalFunctionName(object) // Type.PropertyName -> CanonicalFunctionName() internal override DbExpression Translate(ExpressionConverter parent, MemberExpression call) { PropertyInfo property = (PropertyInfo)call.Member; String canonicalFunctionName = s_propertyRenameMap[property]; DbExpression result; if (call.Expression == null) { result = parent.TranslateIntoCanonicalFunction(canonicalFunctionName, call); } else { result = parent.TranslateIntoCanonicalFunction(canonicalFunctionName, call, call.Expression); } return result; } } private sealed class VBDateAndTimeNowTranslator : PropertyTranslator { private const string s_dateAndTimeTypeFullName = "Microsoft.VisualBasic.DateAndTime"; internal VBDateAndTimeNowTranslator(Assembly vbAssembly) : base(GetProperty(vbAssembly)) { } private static PropertyInfo GetProperty(Assembly vbAssembly) { return vbAssembly.GetType(s_dateAndTimeTypeFullName).GetProperty("Now", BindingFlags.Public | BindingFlags.Static); } // Translation: // Now -> GetDate() internal override DbExpression Translate(ExpressionConverter parent, MemberExpression call) { return parent.TranslateIntoCanonicalFunction(ExpressionConverter.CurrentDateTime, call); } } private sealed class EntityCollectionCountTranslator : PropertyTranslator { internal EntityCollectionCountTranslator() : base(GetProperty()) { } private static PropertyInfo GetProperty() { return typeof(EntityCollection<>).GetProperty(ExpressionConverter.s_entityCollectionCountPropertyName, BindingFlags.Public | BindingFlags.Instance); } // Translation: // EntityCollection.Count -> Count() internal override DbExpression Translate(ExpressionConverter parent, MemberExpression call) { // retranslate as a Count() aggregate, since the name collision prevents us // from calling the method directly in VB and C# return MemberAccessTranslator.TranslateCount(parent, call.Member.DeclaringType.GetGenericArguments()[0], call.Expression); } } private sealed class NullableHasValueTranslator : PropertyTranslator { internal NullableHasValueTranslator() : base(GetProperty()) { } private static PropertyInfo GetProperty() { return typeof(Nullable<>).GetProperty(ExpressionConverter.s_nullableHasValuePropertyName, BindingFlags.Public | BindingFlags.Instance); } // Translation: // Nullable.HasValue -> Not(IsNull(arg)) internal override DbExpression Translate(ExpressionConverter parent, MemberExpression call) { DbExpression argument = parent.TranslateExpression(call.Expression); Debug.Assert(!TypeSemantics.IsCollectionType(argument.ResultType), "Did not expect collection type"); return parent.CreateIsNullExpression(argument, call.Expression.Type).Not(); } } private sealed class NullableValueTranslator : PropertyTranslator { internal NullableValueTranslator() : base(GetProperty()) { } private static PropertyInfo GetProperty() { return typeof(Nullable<>).GetProperty(ExpressionConverter.s_nullableValuePropertyName, BindingFlags.Public | BindingFlags.Instance); } // Translation: // Nullable.Value -> arg internal override DbExpression Translate(ExpressionConverter parent, MemberExpression call) { DbExpression argument = parent.TranslateExpression(call.Expression); Debug.Assert(!TypeSemantics.IsCollectionType(argument.ResultType), "Did not expect collection type"); return argument; } } #endregion } private sealed class ParameterTranslator : TypedTranslator { internal ParameterTranslator() : base(ExpressionType.Parameter) { } protected override DbExpression TypedTranslate(ExpressionConverter parent, ParameterExpression linq) { // Bindings should be intercepted before we get to this point (in ExpressionConverter.TranslateExpression) throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ELinq_UnboundParameterExpression(linq.Name)); } } private sealed class NewTranslator : TypedTranslator { internal NewTranslator() : base(ExpressionType.New) { } protected override DbExpression TypedTranslate(ExpressionConverter parent, NewExpression linq) { int memberCount = null == linq.Members ? 0 : linq.Members.Count; if (null == linq.Constructor || linq.Arguments.Count != memberCount) { throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedConstructor); } parent.CheckInitializerType(linq.Type); List> recordColumns = new List>(memberCount + 1); HashSet memberNames = new HashSet(StringComparer.Ordinal); for (int i = 0; i < memberCount; i++) { string memberName; Type memberType; MemberInfo memberInfo = TypeSystem.PropertyOrField(linq.Members[i], out memberName, out memberType); DbExpression memberValue = parent.TranslateExpression(linq.Arguments[i]); memberNames.Add(memberName); recordColumns.Add(new KeyValuePair(memberName, memberValue)); } InitializerMetadata initializerMetadata; if (0 == memberCount) { // add a sentinel column because CQTs do not accept empty row types recordColumns.Add(DbExpressionBuilder.True.As(KeyColumnName)); initializerMetadata = InitializerMetadata.CreateEmptyProjectionInitializer(parent.EdmItemCollection, linq); } else { // Construct a new initializer type in metadata for this projection (provides the // necessary context for the object materializer) initializerMetadata = InitializerMetadata.CreateProjectionInitializer(parent.EdmItemCollection, linq); } parent.ValidateInitializerMetadata(initializerMetadata); DbNewInstanceExpression projection = parent.CreateNewRowExpression(recordColumns, initializerMetadata); return projection; } } private sealed class NewArrayInitTranslator : TypedTranslator { internal NewArrayInitTranslator() : base(ExpressionType.NewArrayInit) { } protected override DbExpression TypedTranslate(ExpressionConverter parent, NewArrayExpression linq) { if (linq.Expressions.Count > 0) { return DbExpressionBuilder.NewCollection(linq.Expressions.Select(e => parent.TranslateExpression(e))); } TypeUsage typeUsage; if (typeof(byte[]) == linq.Type) { TypeUsage type; if (parent.TryGetValueLayerType(typeof(byte), out type)) { typeUsage = TypeHelpers.CreateCollectionTypeUsage(type); return DbExpressionBuilder.NewEmptyCollection(typeUsage); } } else { if (parent.TryGetValueLayerType(linq.Type, out typeUsage)) { return DbExpressionBuilder.NewEmptyCollection(typeUsage); } } throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedType(DescribeClrType(linq.Type))); } } private sealed class ListInitTranslator : TypedTranslator { internal ListInitTranslator() : base(ExpressionType.ListInit ) { } protected override DbExpression TypedTranslate(ExpressionConverter parent, ListInitExpression linq) { // Ensure requirements: one list initializer argument and a default constructor. if ((linq.NewExpression.Constructor != null) && (linq.NewExpression.Constructor.GetParameters().Length != 0)) { throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedConstructor); } if (linq.Initializers.Any(i => i.Arguments.Count != 1)) { throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedInitializers); } return DbExpressionBuilder.NewCollection(linq.Initializers.Select(i => parent.TranslateExpression(i.Arguments[0]))); } } private sealed class MemberInitTranslator : TypedTranslator { internal MemberInitTranslator() : base(ExpressionType.MemberInit) { } protected override DbExpression TypedTranslate(ExpressionConverter parent, MemberInitExpression linq) { if (null == linq.NewExpression.Constructor || 0 != linq.NewExpression.Constructor.GetParameters().Length) { throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedConstructor); } parent.CheckInitializerType(linq.Type); List> recordColumns = new List>(linq.Bindings.Count + 1); MemberInfo[] members = new MemberInfo[linq.Bindings.Count]; HashSet memberNames = new HashSet(StringComparer.Ordinal); for (int i = 0; i < linq.Bindings.Count; i++) { MemberAssignment binding = linq.Bindings[i] as MemberAssignment; if (null == binding) { throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedBinding); } string memberName; Type memberType; MemberInfo memberInfo = TypeSystem.PropertyOrField(binding.Member, out memberName, out memberType); DbExpression memberValue = parent.TranslateExpression(binding.Expression); memberNames.Add(memberName); members[i] = memberInfo; recordColumns.Add(new KeyValuePair(memberName, memberValue)); } InitializerMetadata initializerMetadata; if (0 == recordColumns.Count) { // add a sentinel column because CQTs do not accept empty row types recordColumns.Add(DbExpressionBuilder.Constant(true).As(KeyColumnName)); initializerMetadata = InitializerMetadata.CreateEmptyProjectionInitializer(parent.EdmItemCollection, linq.NewExpression); } else { // Construct a new initializer type in metadata for this projection (provides the // necessary context for the object materializer) initializerMetadata = InitializerMetadata.CreateProjectionInitializer(parent.EdmItemCollection, linq, members); } parent.ValidateInitializerMetadata(initializerMetadata); DbNewInstanceExpression projection = parent.CreateNewRowExpression(recordColumns, initializerMetadata); return projection; } } private sealed class ConditionalTranslator : TypedTranslator { internal ConditionalTranslator() : base(ExpressionType.Conditional) { } protected override DbExpression TypedTranslate(ExpressionConverter parent, ConditionalExpression linq) { // translate Test ? IfTrue : IfFalse --> CASE WHEN Test THEN IfTrue ELSE IfFalse List whenExpressions = new List(1); whenExpressions.Add(parent.TranslateExpression(linq.Test)); List thenExpressions = new List(1); thenExpressions.Add(parent.TranslateExpression(linq.IfTrue)); DbExpression elseExpression = parent.TranslateExpression(linq.IfFalse); return DbExpressionBuilder.Case(whenExpressions, thenExpressions, elseExpression); } } private sealed class NotSupportedTranslator : Translator { internal NotSupportedTranslator(params ExpressionType[] nodeTypes) : base(nodeTypes) { } internal override DbExpression Translate(ExpressionConverter parent, Expression linq) { throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedExpressionType(linq.NodeType)); } } private sealed class ExtensionTranslator : Translator { internal ExtensionTranslator() : base(EntityExpressionVisitor.CustomExpression) { } internal override DbExpression Translate(ExpressionConverter parent, Expression linq) { QueryParameterExpression queryParameter = linq as QueryParameterExpression; if (null == queryParameter) { throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedExpressionType(linq.NodeType)); } // otherwise add a new query parameter... parent.AddParameter(queryParameter); return queryParameter.ParameterReference; } } #endregion #region Binary expression translators private abstract class BinaryTranslator : TypedTranslator { protected BinaryTranslator(params ExpressionType[] nodeTypes) : base(nodeTypes) { } protected override DbExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq) { return TranslateBinary(parent, parent.TranslateExpression(linq.Left), parent.TranslateExpression(linq.Right), linq); } protected abstract DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq); } private sealed class CoalesceTranslator : BinaryTranslator { internal CoalesceTranslator() : base(ExpressionType.Coalesce) { } protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq) { // left ?? right gets translated to: // CASE WHEN IsNull(left) THEN right ELSE left // construct IsNull DbExpression isNull = parent.CreateIsNullExpression(left, linq.Left.Type); // construct case expression List whenExpressions = new List(1); whenExpressions.Add(isNull); List thenExpressions = new List(1); thenExpressions.Add(right); DbExpression caseExpression = DbExpressionBuilder.Case(whenExpressions, thenExpressions, left); return caseExpression; } } private sealed class AndAlsoTranslator : BinaryTranslator { internal AndAlsoTranslator() : base(ExpressionType.AndAlso) { } protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq) { return left.And(right); } } private sealed class OrElseTranslator : BinaryTranslator { internal OrElseTranslator() : base(ExpressionType.OrElse) { } protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq) { return left.Or(right); } } private sealed class LessThanTranslator : BinaryTranslator { internal LessThanTranslator() : base(ExpressionType.LessThan) { } protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq) { return left.LessThan(right); } } private sealed class LessThanOrEqualsTranslator : BinaryTranslator { internal LessThanOrEqualsTranslator() : base(ExpressionType.LessThanOrEqual) { } protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq) { return left.LessThanOrEqual(right); } } private sealed class GreaterThanTranslator : BinaryTranslator { internal GreaterThanTranslator() : base(ExpressionType.GreaterThan) { } protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq) { return left.GreaterThan(right); } } private sealed class GreaterThanOrEqualsTranslator : BinaryTranslator { internal GreaterThanOrEqualsTranslator() : base(ExpressionType.GreaterThanOrEqual) { } protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq) { return left.GreaterThanOrEqual(right); } } private sealed class EqualsTranslator : TypedTranslator { internal EqualsTranslator() : base(ExpressionType.Equal) { } protected override DbExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq) { Expression linqLeft = linq.Left; Expression linqRight = linq.Right; bool leftIsNull = ExpressionIsNullConstant(linqLeft); bool rightIsNull = ExpressionIsNullConstant(linqRight); // if both values are null, short-circuit if (leftIsNull && rightIsNull) { return DbExpressionBuilder.True; } // if only one side is null, produce an IsNull statement if (leftIsNull) { return CreateIsNullExpression(parent, linqRight); } if (rightIsNull) { return CreateIsNullExpression(parent, linqLeft); } // create a standard equals expression, calling utility method to compensate for null equality DbExpression cqtLeft = parent.TranslateExpression(linqLeft); DbExpression cqtRight = parent.TranslateExpression(linqRight); EqualsPattern pattern = EqualsPattern.Store; if (parent._funcletizer.RootContext.ContextOptions.UseCSharpNullComparisonBehavior) { pattern = EqualsPattern.PositiveNullEqualityComposable; } return parent.CreateEqualsExpression(cqtLeft, cqtRight, pattern, linqLeft.Type, linqRight.Type); } private static DbExpression CreateIsNullExpression(ExpressionConverter parent, Expression input) { input = UnwrapConvert(input); // translate input DbExpression inputCqt = parent.TranslateExpression(input); // create IsNull expression return parent.CreateIsNullExpression(inputCqt, input.Type); } private static bool ExpressionIsNullConstant(Expression expression) { // convert statements introduced by compiler should not affect nullness expression = UnwrapConvert(expression); // check if the unwrapped expression is a null constant if (ExpressionType.Constant != expression.NodeType) { return false; } System.Linq.Expressions.ConstantExpression constant = (System.Linq.Expressions.ConstantExpression)expression; return null == constant.Value; } private static Expression UnwrapConvert(Expression input) { // unwrap all converts while (ExpressionType.Convert == input.NodeType) { input = ((UnaryExpression)input).Operand; } return input; } } private sealed class NotEqualsTranslator : TypedTranslator { internal NotEqualsTranslator() : base(ExpressionType.NotEqual) { } protected override DbExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq) { // rewrite as a not equals expression Expression notLinq = Expression.Not( Expression.Equal(linq.Left, linq.Right)); return parent.TranslateExpression(notLinq); } } #endregion #region Type binary expression translator private sealed class IsTranslator : TypedTranslator { internal IsTranslator() : base(ExpressionType.TypeIs) { } protected override DbExpression TypedTranslate(ExpressionConverter parent, TypeBinaryExpression linq) { DbExpression operand = parent.TranslateExpression(linq.Expression); TypeUsage fromType = operand.ResultType; TypeUsage toType = parent.GetIsOrAsTargetType(fromType, ExpressionType.TypeIs, linq.TypeOperand, linq.Expression.Type); return operand.IsOf(toType); } } #endregion #region Arithmetic expressions private sealed class AddTranslator : BinaryTranslator { internal AddTranslator() : base(ExpressionType.Add, ExpressionType.AddChecked) { } protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq) { if (TypeSemantics.IsPrimitiveType(left.ResultType, PrimitiveTypeKind.String) && TypeSemantics.IsPrimitiveType(right.ResultType, PrimitiveTypeKind.String)) { // Add(string, string) => Concat(string, string) return parent.CreateCanonicalFunction(ExpressionConverter.Concat, linq, left, right); } else { return left.Plus(right); } } } private sealed class DivideTranslator : BinaryTranslator { internal DivideTranslator() : base(ExpressionType.Divide) { } protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq) { return left.Divide(right); } } private sealed class ModuloTranslator : BinaryTranslator { internal ModuloTranslator() : base(ExpressionType.Modulo) { } protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq) { return left.Modulo(right); } } private sealed class MultiplyTranslator : BinaryTranslator { internal MultiplyTranslator() : base(ExpressionType.Multiply, ExpressionType.MultiplyChecked) { } protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq) { return left.Multiply(right); } } private sealed class SubtractTranslator : BinaryTranslator { internal SubtractTranslator() : base(ExpressionType.Subtract, ExpressionType.SubtractChecked) { } protected override DbExpression TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right, BinaryExpression linq) { return left.Minus(right); } } private sealed class NegateTranslator : UnaryTranslator { internal NegateTranslator() : base(ExpressionType.Negate, ExpressionType.NegateChecked) { } protected override DbExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, DbExpression operand) { return operand.UnaryMinus(); } } private sealed class UnaryPlusTranslator : UnaryTranslator { internal UnaryPlusTranslator() : base(ExpressionType.UnaryPlus) { } protected override DbExpression TranslateUnary(ExpressionConverter parent, UnaryExpression unary, DbExpression operand) { // +x = x return operand; } } #endregion #region Bitwise expressions private abstract class BitwiseBinaryTranslator : TypedTranslator { private readonly string _canonicalFunctionName; protected BitwiseBinaryTranslator(ExpressionType nodeType, string canonicalFunctionName) : base(nodeType) { _canonicalFunctionName = canonicalFunctionName; } protected override DbExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq) { DbExpression left = parent.TranslateExpression(linq.Left); DbExpression right = parent.TranslateExpression(linq.Right); //If the arguments are binary we translate into logic expressions if (TypeSemantics.IsBooleanType(left.ResultType)) { return TranslateIntoLogicExpression(parent, linq, left, right); } //Otherwise we translate into bitwise canonical functions return parent.CreateCanonicalFunction(_canonicalFunctionName, linq, left, right); } protected abstract DbExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, DbExpression left, DbExpression right); } private sealed class AndTranslator : BitwiseBinaryTranslator { internal AndTranslator() : base(ExpressionType.And, ExpressionConverter.BitwiseAnd) { } protected override DbExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, DbExpression left, DbExpression right) { return left.And(right); } } private sealed class OrTranslator : BitwiseBinaryTranslator { internal OrTranslator() : base(ExpressionType.Or, ExpressionConverter.BitwiseOr) { } protected override DbExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, DbExpression left, DbExpression right) { return left.Or(right); } } private sealed class ExclusiveOrTranslator : BitwiseBinaryTranslator { internal ExclusiveOrTranslator() : base(ExpressionType.ExclusiveOr, ExpressionConverter.BitwiseXor) { } protected override DbExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, DbExpression left, DbExpression right) { //No direct translation, we translate into ((left && !right) || (!left && right)) DbExpression firstExpression = left.And(right.Not()); DbExpression secondExpression = left.Not().And(right); DbExpression result = firstExpression.Or(secondExpression); return result; } } private sealed class NotTranslator : TypedTranslator { internal NotTranslator() : base(ExpressionType.Not) { } protected override DbExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression linq) { DbExpression operand = parent.TranslateExpression(linq.Operand); if (TypeSemantics.IsBooleanType(operand.ResultType)) { return operand.Not(); } return parent.CreateCanonicalFunction(ExpressionConverter.BitwiseNot, linq, operand); } } #endregion #region Unary expression translators private abstract class UnaryTranslator : TypedTranslator { protected UnaryTranslator(params ExpressionType[] nodeTypes) : base(nodeTypes) { } protected override DbExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression linq) { return TranslateUnary(parent, linq, parent.TranslateExpression(linq.Operand)); } protected abstract DbExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, DbExpression operand); } private sealed class QuoteTranslator : UnaryTranslator { internal QuoteTranslator() : base(ExpressionType.Quote) { } protected override DbExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, DbExpression operand) { // simply return the operand: expressions compilations not cached for LINQ, so // parameters are always bound properly return operand; } } private sealed class ConvertTranslator : UnaryTranslator { internal ConvertTranslator() : base(ExpressionType.Convert, ExpressionType.ConvertChecked) { } protected override DbExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, DbExpression operand) { Type toClrType = unary.Type; Type fromClrType = unary.Operand.Type; DbExpression cast = parent.CreateCastExpression(operand, toClrType, fromClrType); return cast; } } private sealed class AsTranslator : UnaryTranslator { internal AsTranslator() : base(ExpressionType.TypeAs) { } protected override DbExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, DbExpression operand) { TypeUsage fromType = operand.ResultType; TypeUsage toType = parent.GetIsOrAsTargetType(fromType, ExpressionType.TypeAs, unary.Type, unary.Operand.Type); return operand.TreatAs(toType); } } #endregion } }