Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / Objects / ELinq / Translator.cs / 1 / Translator.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....], [....] //--------------------------------------------------------------------- using System.Data.Common.CommandTrees; using System.Collections.Generic; using CqtExpression = System.Data.Common.CommandTrees.DbExpression; using LinqExpression = System.Linq.Expressions.Expression; using System.Diagnostics; using System.Data.Metadata.Edm; using System.Linq.Expressions; using System.Reflection; using System.Data.Objects.DataClasses; using System.Globalization; using System.Data.Entity; namespace System.Data.Objects.ELinq { 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 IEnumerableNodeTypes { get { return _nodeTypes; } } internal abstract CqtExpression Translate(ExpressionConverter parent, LinqExpression linq); public override string ToString() { return this.GetType().Name; } } #region Misc // Typed version of Translator private abstract class TypedTranslator : Translator where T_Linq : LinqExpression { protected TypedTranslator(params ExpressionType[] nodeTypes) : base(nodeTypes) { } internal override CqtExpression Translate(ExpressionConverter parent, LinqExpression linq) { return TypedTranslate(parent, (T_Linq)linq); } protected abstract CqtExpression TypedTranslate(ExpressionConverter parent, T_Linq linq); } private sealed class ConstantTranslator : TypedTranslator { internal ConstantTranslator() : base(ExpressionType.Constant) { } protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.ConstantExpression linq) { ObjectQuery queryOfT = linq.Value as ObjectQuery; if (null != queryOfT) { return parent.TranslateInlineQueryOfT(queryOfT); } 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 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 (type.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType || (isNullValue && type.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType)) { typeSupported = true; } } if (!typeSupported) { throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedConstant(DescribeClrType(linq.Type), Strings.ELinq_PrimitiveTypesSample)); } // create a constant or null expression depending on value if (isNullValue) { return parent._commandTree.CreateNullExpression(type); } else { return parent._commandTree.CreateConstantExpression(linq.Value, type); } } } private sealed 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 CqtExpression TypedTranslate(ExpressionConverter parent, MemberExpression linq) { CqtExpression 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) { CqtExpression 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 proeperties 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 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; } } if (s_propertyTranslators.TryGetValue(propertyInfo, out propertyTranslator)) { 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 return s_propertyTranslators.TryGetValue(propertyInfo, out propertyTranslator); } } 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, CqtExpression instance, out CqtExpression 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 = parent._commandTree.CreatePropertyExpression(name, instance); 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._typeResolver.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 { // if we're accessing a key member of a navigation, collapse the structure instance // to the key reference. instance = NormalizeInstanceForReference(parent, instance, member); propertyExpression = parent._commandTree.CreatePropertyExpression(name, instance); 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 = parent._commandTree.CreatePropertyExpression(ExpressionConverter.KeyColumnName, property.Instance); return true; } } } propertyExpression = null; return false; } /// /// We simplify the property instance where the user is accessing a key member of /// a reference navigation. The instance becomes simply the reference key in such /// cases. /// /// For instance, product.Category.CategoryID becomes Ref(product.Category).CategoryID, /// which gives us a chance of optimizing the query (using foreign keys rather than joins) /// /// Conversion context. /// Instance from which we're accessing member. /// Member we're accessing. ///'Simplified' instance. If the member is a key and the instance is a navigation /// the returned instance is a reference navigation rather than the full entity. private static CqtExpression NormalizeInstanceForReference(ExpressionConverter parent, CqtExpression instance, EdmMember member) { EdmType instanceType = instance.ResultType.EdmType; if (instanceType.BuiltInTypeKind == BuiltInTypeKind.EntityType) { EntityType entityType = (EntityType)instanceType; if (entityType.KeyMembers.Contains(member) && instance.ExpressionKind == DbExpressionKind.Property) { DbPropertyExpression propertyExpression = (DbPropertyExpression)instance; if (propertyExpression.Property.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty) { // modify the property expression so that it merely retrieves the reference // not the entire entity NavigationProperty navigationProperty = (NavigationProperty)propertyExpression.Property; DbExpression navigationSource = parent._commandTree.CreateEntityRefExpression(propertyExpression.Instance); DbExpression navigationExpression = parent._commandTree.CreateRelationshipNavigationExpression( navigationProperty.FromEndMember, navigationProperty.ToEndMember, navigationSource); instance = parent._commandTree.CreateRefKeyExpression(navigationExpression); } } } return instance; } private static CqtExpression TranslateNavigationProperty(ExpressionConverter parent, MemberInfo clrMember, CqtExpression instance, NavigationProperty navProp) { CqtExpression propertyExpression; propertyExpression = parent._commandTree.CreatePropertyExpression(navProp, instance); // for EntityCollection navigations, wrap in "grouping" where the key is the parent // entity and the group contains the child entities if (BuiltInTypeKind.CollectionType == propertyExpression.ResultType.EdmType.BuiltInTypeKind) { 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 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(); } /// /// Checks whether the given property info can be translated into a canonical function. /// 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 CanTranslatePropertyInfo(PropertyInfo propertyInfo) { PropertyTranslator propertyTranslator; return TryGetTranslator(propertyInfo, out propertyTranslator); } #endregion #region 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 CqtExpression 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 CqtExpression 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 CqtExpression Translate(ExpressionConverter parent, MemberExpression call) { PropertyInfo property = (PropertyInfo)call.Member; String canonicalFunctionName = s_propertyRenameMap[property]; CqtExpression 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 CqtExpression 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 CqtExpression 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# MethodInfo countMethod; ReflectionUtil.TryLookupMethod(SequenceMethod.Count, out countMethod); Debug.Assert(null != countMethod, "Count() must exist"); countMethod = countMethod.MakeGenericMethod(call.Member.DeclaringType.GetGenericArguments()); LinqExpression countCall = LinqExpression.Call(countMethod, call.Expression); return parent.TranslateExpression(countCall); } } 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 CqtExpression Translate(ExpressionConverter parent, MemberExpression call) { CqtExpression argument = parent.TranslateExpression(call.Expression); Debug.Assert(!TypeSemantics.IsCollectionType(argument.ResultType), "Did not expect collection type"); return parent._commandTree.CreateNotExpression(parent.CreateIsNullExpression(argument, call.Expression.Type)); } } 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 CqtExpression Translate(ExpressionConverter parent, MemberExpression call) { CqtExpression 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 CqtExpression TypedTranslate(ExpressionConverter parent, ParameterExpression linq) { // Bindings should be intercepted before we get to this point (in ExpressionConverter.TranslateExpression) if (parent._bindingContext.IsRootContextParameter(linq)) { throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ELinq_UnsupportedUseOfContextParameter(linq.Name)); } else { 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); CqtExpression 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(new KeyValuePair (KeyColumnName, parent._commandTree.CreateTrueExpression())); 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 MemberInitTranslator : TypedTranslator { internal MemberInitTranslator() : base(ExpressionType.MemberInit) { } protected override CqtExpression 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); CqtExpression 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(new KeyValuePair (KeyColumnName, parent._commandTree.CreateTrueExpression())); 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 CqtExpression 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)); CqtExpression elseExpression = parent.TranslateExpression(linq.IfFalse); return parent._commandTree.CreateCaseExpression(whenExpressions, thenExpressions, elseExpression); } } private sealed class NotSupportedTranslator : Translator { internal NotSupportedTranslator(params ExpressionType[] nodeTypes) : base(nodeTypes) { } internal override CqtExpression Translate(ExpressionConverter parent, LinqExpression linq) { throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedExpressionType(linq.NodeType)); } } #endregion #region Binary expression translators private abstract class BinaryTranslator : TypedTranslator { protected BinaryTranslator(params ExpressionType[] nodeTypes) : base(nodeTypes) { } protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq) { return TranslateBinary(parent, parent.TranslateExpression(linq.Left), parent.TranslateExpression(linq.Right), linq); } protected abstract CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq); } private sealed class CoalesceTranslator : BinaryTranslator { internal CoalesceTranslator() : base(ExpressionType.Coalesce) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { // left ?? right gets translated to: // CASE WHEN IsNull(left) THEN right ELSE left // construct IsNull CqtExpression 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); CqtExpression caseExpression = parent._commandTree.CreateCaseExpression(whenExpressions, thenExpressions, left); return caseExpression; } } private sealed class AndAlsoTranslator : BinaryTranslator { internal AndAlsoTranslator() : base(ExpressionType.AndAlso) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateAndExpression(left, right); } } private sealed class OrElseTranslator : BinaryTranslator { internal OrElseTranslator() : base(ExpressionType.OrElse) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateOrExpression(left, right); } } private sealed class LessThanTranslator : BinaryTranslator { internal LessThanTranslator() : base(ExpressionType.LessThan) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateLessThanExpression(left, right); } } private sealed class LessThanOrEqualsTranslator : BinaryTranslator { internal LessThanOrEqualsTranslator() : base(ExpressionType.LessThanOrEqual) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateLessThanOrEqualsExpression(left, right); } } private sealed class GreaterThanTranslator : BinaryTranslator { internal GreaterThanTranslator() : base(ExpressionType.GreaterThan) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateGreaterThanExpression(left, right); } } private sealed class GreaterThanOrEqualsTranslator : BinaryTranslator { internal GreaterThanOrEqualsTranslator() : base(ExpressionType.GreaterThanOrEqual) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateGreaterThanOrEqualsExpression(left, right); } } private sealed class EqualsTranslator : TypedTranslator { internal EqualsTranslator() : base(ExpressionType.Equal) { } protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq) { Expression linqLeft = linq.Left; Expression linqRight = linq.Right; bool leftIsNull = LinqExpressionIsNullConstant(linqLeft); bool rightIsNull = LinqExpressionIsNullConstant(linqRight); // if both values are null, short-circuit if (leftIsNull && rightIsNull) { return parent._commandTree.CreateTrueExpression(); } // 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 CqtExpression cqtLeft = parent.TranslateExpression(linqLeft); CqtExpression cqtRight = parent.TranslateExpression(linqRight); return parent.CreateEqualsExpression(cqtLeft, cqtRight, EqualsPattern.Store, linqLeft.Type, linqRight.Type); } private static CqtExpression CreateIsNullExpression(ExpressionConverter parent, LinqExpression input) { input = UnwrapConvert(input); // translate input CqtExpression inputCqt = parent.TranslateExpression(input); // create IsNull expression return parent.CreateIsNullExpression(inputCqt, input.Type); } private static bool LinqExpressionIsNullConstant(LinqExpression 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 LinqExpression UnwrapConvert(LinqExpression 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 CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq) { // rewrite as a not equals expression LinqExpression notLinq = LinqExpression.Not( LinqExpression.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 CqtExpression TypedTranslate(ExpressionConverter parent, TypeBinaryExpression linq) { CqtExpression operand = parent.TranslateExpression(linq.Expression); TypeUsage fromType = operand.ResultType; TypeUsage toType = parent.GetIsOrAsTargetType(fromType, ExpressionType.TypeIs, linq.TypeOperand, linq.Expression.Type); return parent._commandTree.CreateIsOfExpression(operand, toType); } } #endregion #region Arithmetic expressions private sealed class AddTranslator : BinaryTranslator { internal AddTranslator() : base(ExpressionType.Add, ExpressionType.AddChecked) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression 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 parent._commandTree.CreatePlusExpression(left, right); } } } private sealed class DivideTranslator : BinaryTranslator { internal DivideTranslator() : base(ExpressionType.Divide) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateDivideExpression(left, right); } } private sealed class ModuloTranslator : BinaryTranslator { internal ModuloTranslator() : base(ExpressionType.Modulo) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateModuloExpression(left, right); } } private sealed class MultiplyTranslator : BinaryTranslator { internal MultiplyTranslator() : base(ExpressionType.Multiply, ExpressionType.MultiplyChecked) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateMultiplyExpression(left, right); } } private sealed class SubtractTranslator : BinaryTranslator { internal SubtractTranslator() : base(ExpressionType.Subtract, ExpressionType.SubtractChecked) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateMinusExpression(left, right); } } private sealed class NegateTranslator : UnaryTranslator { internal NegateTranslator() : base(ExpressionType.Negate, ExpressionType.NegateChecked) { } protected override CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand) { return parent._commandTree.CreateUnaryMinusExpression(operand); } } 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 CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq) { CqtExpression left = parent.TranslateExpression(linq.Left); CqtExpression 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 CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right); } private sealed class AndTranslator : BitwiseBinaryTranslator { internal AndTranslator() : base(ExpressionType.And, ExpressionConverter.BitwiseAnd) { } protected override CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right) { return parent._commandTree.CreateAndExpression(left, right); } } private sealed class OrTranslator : BitwiseBinaryTranslator { internal OrTranslator() : base(ExpressionType.Or, ExpressionConverter.BitwiseOr) { } protected override CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right) { return parent._commandTree.CreateOrExpression(left, right); } } private sealed class ExclusiveOrTranslator : BitwiseBinaryTranslator { internal ExclusiveOrTranslator() : base(ExpressionType.ExclusiveOr, ExpressionConverter.BitwiseXor) { } protected override CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right) { //No direct translation, we translate into ((left && !right) || (!left && right)) CqtExpression firstExpression = parent._commandTree.CreateAndExpression( left, parent._commandTree.CreateNotExpression(right)); CqtExpression secondExpression = parent._commandTree.CreateAndExpression( parent._commandTree.CreateNotExpression(parent.TranslateExpression(linq.Left)), parent.TranslateExpression(linq.Right)); CqtExpression result = parent._commandTree.CreateOrExpression(firstExpression, secondExpression); return result; } } private sealed class NotTranslator : TypedTranslator { internal NotTranslator() : base(ExpressionType.Not) { } protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression linq) { CqtExpression operand = parent.TranslateExpression(linq.Operand); if (TypeSemantics.IsBooleanType(operand.ResultType)) { return parent._commandTree.CreateNotExpression(operand); } 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 CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression linq) { return TranslateUnary(parent, linq, parent.TranslateExpression(linq.Operand)); } protected abstract CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand); } private sealed class QuoteTranslator : UnaryTranslator { internal QuoteTranslator() : base(ExpressionType.Quote) { } protected override CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression 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 CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand) { Type toClrType = unary.Type; Type fromClrType = unary.Operand.Type; CqtExpression source = parent.TranslateExpression(unary.Operand); return parent.CreateCastExpression(source, toClrType, fromClrType); } } private sealed class AsTranslator : UnaryTranslator { internal AsTranslator() : base(ExpressionType.TypeAs) { } protected override CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand) { TypeUsage fromType = operand.ResultType; TypeUsage toType = parent.GetIsOrAsTargetType(fromType, ExpressionType.TypeAs, unary.Type, unary.Operand.Type); return parent._commandTree.CreateTreatExpression(operand, toType); } } #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....], [....] //--------------------------------------------------------------------- using System.Data.Common.CommandTrees; using System.Collections.Generic; using CqtExpression = System.Data.Common.CommandTrees.DbExpression; using LinqExpression = System.Linq.Expressions.Expression; using System.Diagnostics; using System.Data.Metadata.Edm; using System.Linq.Expressions; using System.Reflection; using System.Data.Objects.DataClasses; using System.Globalization; using System.Data.Entity; namespace System.Data.Objects.ELinq { 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 IEnumerableNodeTypes { get { return _nodeTypes; } } internal abstract CqtExpression Translate(ExpressionConverter parent, LinqExpression linq); public override string ToString() { return this.GetType().Name; } } #region Misc // Typed version of Translator private abstract class TypedTranslator : Translator where T_Linq : LinqExpression { protected TypedTranslator(params ExpressionType[] nodeTypes) : base(nodeTypes) { } internal override CqtExpression Translate(ExpressionConverter parent, LinqExpression linq) { return TypedTranslate(parent, (T_Linq)linq); } protected abstract CqtExpression TypedTranslate(ExpressionConverter parent, T_Linq linq); } private sealed class ConstantTranslator : TypedTranslator { internal ConstantTranslator() : base(ExpressionType.Constant) { } protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.ConstantExpression linq) { ObjectQuery queryOfT = linq.Value as ObjectQuery; if (null != queryOfT) { return parent.TranslateInlineQueryOfT(queryOfT); } 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 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 (type.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType || (isNullValue && type.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType)) { typeSupported = true; } } if (!typeSupported) { throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedConstant(DescribeClrType(linq.Type), Strings.ELinq_PrimitiveTypesSample)); } // create a constant or null expression depending on value if (isNullValue) { return parent._commandTree.CreateNullExpression(type); } else { return parent._commandTree.CreateConstantExpression(linq.Value, type); } } } private sealed 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 CqtExpression TypedTranslate(ExpressionConverter parent, MemberExpression linq) { CqtExpression 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) { CqtExpression 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 proeperties 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 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; } } if (s_propertyTranslators.TryGetValue(propertyInfo, out propertyTranslator)) { 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 return s_propertyTranslators.TryGetValue(propertyInfo, out propertyTranslator); } } 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, CqtExpression instance, out CqtExpression 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 = parent._commandTree.CreatePropertyExpression(name, instance); 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._typeResolver.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 { // if we're accessing a key member of a navigation, collapse the structure instance // to the key reference. instance = NormalizeInstanceForReference(parent, instance, member); propertyExpression = parent._commandTree.CreatePropertyExpression(name, instance); 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 = parent._commandTree.CreatePropertyExpression(ExpressionConverter.KeyColumnName, property.Instance); return true; } } } propertyExpression = null; return false; } /// /// We simplify the property instance where the user is accessing a key member of /// a reference navigation. The instance becomes simply the reference key in such /// cases. /// /// For instance, product.Category.CategoryID becomes Ref(product.Category).CategoryID, /// which gives us a chance of optimizing the query (using foreign keys rather than joins) /// /// Conversion context. /// Instance from which we're accessing member. /// Member we're accessing. ///'Simplified' instance. If the member is a key and the instance is a navigation /// the returned instance is a reference navigation rather than the full entity. private static CqtExpression NormalizeInstanceForReference(ExpressionConverter parent, CqtExpression instance, EdmMember member) { EdmType instanceType = instance.ResultType.EdmType; if (instanceType.BuiltInTypeKind == BuiltInTypeKind.EntityType) { EntityType entityType = (EntityType)instanceType; if (entityType.KeyMembers.Contains(member) && instance.ExpressionKind == DbExpressionKind.Property) { DbPropertyExpression propertyExpression = (DbPropertyExpression)instance; if (propertyExpression.Property.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty) { // modify the property expression so that it merely retrieves the reference // not the entire entity NavigationProperty navigationProperty = (NavigationProperty)propertyExpression.Property; DbExpression navigationSource = parent._commandTree.CreateEntityRefExpression(propertyExpression.Instance); DbExpression navigationExpression = parent._commandTree.CreateRelationshipNavigationExpression( navigationProperty.FromEndMember, navigationProperty.ToEndMember, navigationSource); instance = parent._commandTree.CreateRefKeyExpression(navigationExpression); } } } return instance; } private static CqtExpression TranslateNavigationProperty(ExpressionConverter parent, MemberInfo clrMember, CqtExpression instance, NavigationProperty navProp) { CqtExpression propertyExpression; propertyExpression = parent._commandTree.CreatePropertyExpression(navProp, instance); // for EntityCollection navigations, wrap in "grouping" where the key is the parent // entity and the group contains the child entities if (BuiltInTypeKind.CollectionType == propertyExpression.ResultType.EdmType.BuiltInTypeKind) { 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 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(); } /// /// Checks whether the given property info can be translated into a canonical function. /// 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 CanTranslatePropertyInfo(PropertyInfo propertyInfo) { PropertyTranslator propertyTranslator; return TryGetTranslator(propertyInfo, out propertyTranslator); } #endregion #region 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 CqtExpression 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 CqtExpression 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 CqtExpression Translate(ExpressionConverter parent, MemberExpression call) { PropertyInfo property = (PropertyInfo)call.Member; String canonicalFunctionName = s_propertyRenameMap[property]; CqtExpression 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 CqtExpression 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 CqtExpression 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# MethodInfo countMethod; ReflectionUtil.TryLookupMethod(SequenceMethod.Count, out countMethod); Debug.Assert(null != countMethod, "Count() must exist"); countMethod = countMethod.MakeGenericMethod(call.Member.DeclaringType.GetGenericArguments()); LinqExpression countCall = LinqExpression.Call(countMethod, call.Expression); return parent.TranslateExpression(countCall); } } 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 CqtExpression Translate(ExpressionConverter parent, MemberExpression call) { CqtExpression argument = parent.TranslateExpression(call.Expression); Debug.Assert(!TypeSemantics.IsCollectionType(argument.ResultType), "Did not expect collection type"); return parent._commandTree.CreateNotExpression(parent.CreateIsNullExpression(argument, call.Expression.Type)); } } 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 CqtExpression Translate(ExpressionConverter parent, MemberExpression call) { CqtExpression 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 CqtExpression TypedTranslate(ExpressionConverter parent, ParameterExpression linq) { // Bindings should be intercepted before we get to this point (in ExpressionConverter.TranslateExpression) if (parent._bindingContext.IsRootContextParameter(linq)) { throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ELinq_UnsupportedUseOfContextParameter(linq.Name)); } else { 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); CqtExpression 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(new KeyValuePair (KeyColumnName, parent._commandTree.CreateTrueExpression())); 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 MemberInitTranslator : TypedTranslator { internal MemberInitTranslator() : base(ExpressionType.MemberInit) { } protected override CqtExpression 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); CqtExpression 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(new KeyValuePair (KeyColumnName, parent._commandTree.CreateTrueExpression())); 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 CqtExpression 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)); CqtExpression elseExpression = parent.TranslateExpression(linq.IfFalse); return parent._commandTree.CreateCaseExpression(whenExpressions, thenExpressions, elseExpression); } } private sealed class NotSupportedTranslator : Translator { internal NotSupportedTranslator(params ExpressionType[] nodeTypes) : base(nodeTypes) { } internal override CqtExpression Translate(ExpressionConverter parent, LinqExpression linq) { throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedExpressionType(linq.NodeType)); } } #endregion #region Binary expression translators private abstract class BinaryTranslator : TypedTranslator { protected BinaryTranslator(params ExpressionType[] nodeTypes) : base(nodeTypes) { } protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq) { return TranslateBinary(parent, parent.TranslateExpression(linq.Left), parent.TranslateExpression(linq.Right), linq); } protected abstract CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq); } private sealed class CoalesceTranslator : BinaryTranslator { internal CoalesceTranslator() : base(ExpressionType.Coalesce) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { // left ?? right gets translated to: // CASE WHEN IsNull(left) THEN right ELSE left // construct IsNull CqtExpression 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); CqtExpression caseExpression = parent._commandTree.CreateCaseExpression(whenExpressions, thenExpressions, left); return caseExpression; } } private sealed class AndAlsoTranslator : BinaryTranslator { internal AndAlsoTranslator() : base(ExpressionType.AndAlso) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateAndExpression(left, right); } } private sealed class OrElseTranslator : BinaryTranslator { internal OrElseTranslator() : base(ExpressionType.OrElse) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateOrExpression(left, right); } } private sealed class LessThanTranslator : BinaryTranslator { internal LessThanTranslator() : base(ExpressionType.LessThan) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateLessThanExpression(left, right); } } private sealed class LessThanOrEqualsTranslator : BinaryTranslator { internal LessThanOrEqualsTranslator() : base(ExpressionType.LessThanOrEqual) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateLessThanOrEqualsExpression(left, right); } } private sealed class GreaterThanTranslator : BinaryTranslator { internal GreaterThanTranslator() : base(ExpressionType.GreaterThan) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateGreaterThanExpression(left, right); } } private sealed class GreaterThanOrEqualsTranslator : BinaryTranslator { internal GreaterThanOrEqualsTranslator() : base(ExpressionType.GreaterThanOrEqual) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateGreaterThanOrEqualsExpression(left, right); } } private sealed class EqualsTranslator : TypedTranslator { internal EqualsTranslator() : base(ExpressionType.Equal) { } protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq) { Expression linqLeft = linq.Left; Expression linqRight = linq.Right; bool leftIsNull = LinqExpressionIsNullConstant(linqLeft); bool rightIsNull = LinqExpressionIsNullConstant(linqRight); // if both values are null, short-circuit if (leftIsNull && rightIsNull) { return parent._commandTree.CreateTrueExpression(); } // 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 CqtExpression cqtLeft = parent.TranslateExpression(linqLeft); CqtExpression cqtRight = parent.TranslateExpression(linqRight); return parent.CreateEqualsExpression(cqtLeft, cqtRight, EqualsPattern.Store, linqLeft.Type, linqRight.Type); } private static CqtExpression CreateIsNullExpression(ExpressionConverter parent, LinqExpression input) { input = UnwrapConvert(input); // translate input CqtExpression inputCqt = parent.TranslateExpression(input); // create IsNull expression return parent.CreateIsNullExpression(inputCqt, input.Type); } private static bool LinqExpressionIsNullConstant(LinqExpression 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 LinqExpression UnwrapConvert(LinqExpression 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 CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq) { // rewrite as a not equals expression LinqExpression notLinq = LinqExpression.Not( LinqExpression.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 CqtExpression TypedTranslate(ExpressionConverter parent, TypeBinaryExpression linq) { CqtExpression operand = parent.TranslateExpression(linq.Expression); TypeUsage fromType = operand.ResultType; TypeUsage toType = parent.GetIsOrAsTargetType(fromType, ExpressionType.TypeIs, linq.TypeOperand, linq.Expression.Type); return parent._commandTree.CreateIsOfExpression(operand, toType); } } #endregion #region Arithmetic expressions private sealed class AddTranslator : BinaryTranslator { internal AddTranslator() : base(ExpressionType.Add, ExpressionType.AddChecked) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression 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 parent._commandTree.CreatePlusExpression(left, right); } } } private sealed class DivideTranslator : BinaryTranslator { internal DivideTranslator() : base(ExpressionType.Divide) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateDivideExpression(left, right); } } private sealed class ModuloTranslator : BinaryTranslator { internal ModuloTranslator() : base(ExpressionType.Modulo) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateModuloExpression(left, right); } } private sealed class MultiplyTranslator : BinaryTranslator { internal MultiplyTranslator() : base(ExpressionType.Multiply, ExpressionType.MultiplyChecked) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateMultiplyExpression(left, right); } } private sealed class SubtractTranslator : BinaryTranslator { internal SubtractTranslator() : base(ExpressionType.Subtract, ExpressionType.SubtractChecked) { } protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq) { return parent._commandTree.CreateMinusExpression(left, right); } } private sealed class NegateTranslator : UnaryTranslator { internal NegateTranslator() : base(ExpressionType.Negate, ExpressionType.NegateChecked) { } protected override CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand) { return parent._commandTree.CreateUnaryMinusExpression(operand); } } 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 CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq) { CqtExpression left = parent.TranslateExpression(linq.Left); CqtExpression 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 CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right); } private sealed class AndTranslator : BitwiseBinaryTranslator { internal AndTranslator() : base(ExpressionType.And, ExpressionConverter.BitwiseAnd) { } protected override CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right) { return parent._commandTree.CreateAndExpression(left, right); } } private sealed class OrTranslator : BitwiseBinaryTranslator { internal OrTranslator() : base(ExpressionType.Or, ExpressionConverter.BitwiseOr) { } protected override CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right) { return parent._commandTree.CreateOrExpression(left, right); } } private sealed class ExclusiveOrTranslator : BitwiseBinaryTranslator { internal ExclusiveOrTranslator() : base(ExpressionType.ExclusiveOr, ExpressionConverter.BitwiseXor) { } protected override CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right) { //No direct translation, we translate into ((left && !right) || (!left && right)) CqtExpression firstExpression = parent._commandTree.CreateAndExpression( left, parent._commandTree.CreateNotExpression(right)); CqtExpression secondExpression = parent._commandTree.CreateAndExpression( parent._commandTree.CreateNotExpression(parent.TranslateExpression(linq.Left)), parent.TranslateExpression(linq.Right)); CqtExpression result = parent._commandTree.CreateOrExpression(firstExpression, secondExpression); return result; } } private sealed class NotTranslator : TypedTranslator { internal NotTranslator() : base(ExpressionType.Not) { } protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression linq) { CqtExpression operand = parent.TranslateExpression(linq.Operand); if (TypeSemantics.IsBooleanType(operand.ResultType)) { return parent._commandTree.CreateNotExpression(operand); } 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 CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression linq) { return TranslateUnary(parent, linq, parent.TranslateExpression(linq.Operand)); } protected abstract CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand); } private sealed class QuoteTranslator : UnaryTranslator { internal QuoteTranslator() : base(ExpressionType.Quote) { } protected override CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression 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 CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand) { Type toClrType = unary.Type; Type fromClrType = unary.Operand.Type; CqtExpression source = parent.TranslateExpression(unary.Operand); return parent.CreateCastExpression(source, toClrType, fromClrType); } } private sealed class AsTranslator : UnaryTranslator { internal AsTranslator() : base(ExpressionType.TypeAs) { } protected override CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand) { TypeUsage fromType = operand.ResultType; TypeUsage toType = parent.GetIsOrAsTargetType(fromType, ExpressionType.TypeAs, unary.Type, unary.Operand.Type); return parent._commandTree.CreateTreatExpression(operand, toType); } } #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XmlDigitalSignatureProcessor.cs
- ProgressBar.cs
- HuffModule.cs
- LayoutTable.cs
- DataGridHeaderBorder.cs
- sqlser.cs
- XmlSchemaObjectCollection.cs
- EntityRecordInfo.cs
- TranslateTransform3D.cs
- DBSchemaRow.cs
- TextRunProperties.cs
- DataGridSortCommandEventArgs.cs
- SamlSerializer.cs
- UnSafeCharBuffer.cs
- DelegatingTypeDescriptionProvider.cs
- TreeNodeBinding.cs
- TreeNodeEventArgs.cs
- PriorityBinding.cs
- TabControl.cs
- Axis.cs
- XmlNodeComparer.cs
- DataSourceHelper.cs
- HitTestWithPointDrawingContextWalker.cs
- TcpActivation.cs
- EventProxy.cs
- BitStream.cs
- PassportAuthenticationEventArgs.cs
- PartialTrustVisibleAssemblyCollection.cs
- Rfc2898DeriveBytes.cs
- FileUtil.cs
- XmlDocument.cs
- DataListItem.cs
- X509SecurityTokenAuthenticator.cs
- DataGridAutomationPeer.cs
- PrintPreviewDialog.cs
- Italic.cs
- XmlUtilWriter.cs
- XsdDuration.cs
- FileBasedResourceGroveler.cs
- DBSchemaRow.cs
- MembershipValidatePasswordEventArgs.cs
- TrackingRecord.cs
- FileAuthorizationModule.cs
- Classification.cs
- BamlVersionHeader.cs
- COM2IDispatchConverter.cs
- PropertyEmitterBase.cs
- LoadGrammarCompletedEventArgs.cs
- AccessibilityHelperForVista.cs
- WebHeaderCollection.cs
- Types.cs
- MsmqIntegrationBinding.cs
- TerminateDesigner.cs
- PropertyRecord.cs
- WSTrust.cs
- RegexTree.cs
- Configuration.cs
- SplitterEvent.cs
- ValuePatternIdentifiers.cs
- BitVector32.cs
- ReferentialConstraint.cs
- RefreshPropertiesAttribute.cs
- EastAsianLunisolarCalendar.cs
- SystemParameters.cs
- LinkDescriptor.cs
- PnrpPermission.cs
- DataBindingHandlerAttribute.cs
- XmlNodeChangedEventArgs.cs
- EmptyStringExpandableObjectConverter.cs
- FacetValueContainer.cs
- ItemsControl.cs
- PropertyItem.cs
- AppSettingsSection.cs
- AbstractDataSvcMapFileLoader.cs
- SiteMapProvider.cs
- MethodSet.cs
- CompositeActivityDesigner.cs
- BrowserCapabilitiesCompiler.cs
- DataControlImageButton.cs
- CreateDataSourceDialog.cs
- AdapterDictionary.cs
- X509ChainElement.cs
- ScriptRegistrationManager.cs
- UserControlBuildProvider.cs
- DefaultBinder.cs
- RemoteWebConfigurationHostServer.cs
- XPathNodeList.cs
- LinqDataView.cs
- SqlDataSourceCommandEventArgs.cs
- CqlGenerator.cs
- CalloutQueueItem.cs
- UserValidatedEventArgs.cs
- lengthconverter.cs
- Point3D.cs
- HttpException.cs
- OracleRowUpdatedEventArgs.cs
- HttpPostLocalhostServerProtocol.cs
- PartialCachingControl.cs
- BamlTreeMap.cs
- XamlStream.cs