File: System\Data\Objects\ELinq\Translator.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="Translator.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner  Microsoft, Microsoft
//---------------------------------------------------------------------
 
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<ExpressionType> 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<T_Linq> : 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<System.Linq.Expressions.ConstantExpression>
        {
            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<Expression>();
                        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<MemberExpression>
        {
            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<PropertyInfo, PropertyTranslator> 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<PropertyInfo, PropertyTranslator>();
                foreach (PropertyTranslator translator in GetPropertyTranslators())
                {
                    foreach (PropertyInfo property in translator.Properties)
                    {
                        s_propertyTranslators.Add(property, translator);
                    }
                }
            }
 
            /// <summary>
            /// 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
            /// </summary>
            /// <param name="propertyInfo"></param>
            /// <param name="propertyTranslator"></param>
            /// <returns></returns>
            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<KeyValuePair<string, DbExpression>> collectionColumns =
                            new List<KeyValuePair<string, DbExpression>>(2);
                        collectionColumns.Add(new KeyValuePair<string, DbExpression>(
                            ExpressionConverter.EntityCollectionOwnerColumnName, instance));
                        collectionColumns.Add(new KeyValuePair<string, DbExpression>(
                            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<PropertyTranslator> GetVisualBasicPropertyTranslators(Assembly vbAssembly)
            {
                yield return new VBDateAndTimeNowTranslator(vbAssembly);
            }
 
            private static IEnumerable<PropertyTranslator> 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();
            }
 
            /// <summary>
            /// 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
            /// </summary>
            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<PropertyInfo>())
                {
                    _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<T> first
                    // before calling the more expensive TypeSystem.FindICollection to test whether the declaring type
                    // of the property implements ICollection<T>.
 
                    //
                    // Int32 Count
                    //
                    if (propertyInfo.Name == "Count" &&
                        propertyInfo.PropertyType.Equals(typeof(int)))
                    {
                        foreach (KeyValuePair<Type, Type> 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<T> 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<KeyValuePair<Type, Type>> GetImplementedICollections(Type type)
                {
                    Type collectionElementType;
                    if (IsICollection(type, out collectionElementType))
                    {
                        yield return new KeyValuePair<Type, Type>(type, collectionElementType);
                    }
                    else
                    {
                        foreach (Type interfaceType in type.GetInterfaces())
                        {
                            if (IsICollection(interfaceType, out collectionElementType))
                            {
                                yield return new KeyValuePair<Type, Type>(interfaceType, collectionElementType);
                            }
                        }
                    }
                }
            }
 
            #endregion
 
            #region Signature-based Property Translators
            private abstract class PropertyTranslator
            {
                private readonly IEnumerable<PropertyInfo> _properties;
                protected PropertyTranslator(params PropertyInfo[] properties) { _properties = properties; }
                protected PropertyTranslator(IEnumerable<PropertyInfo> properties) { _properties = properties; }
                internal IEnumerable<PropertyInfo> 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<PropertyInfo> 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<PropertyInfo, string> s_propertyRenameMap = new Dictionary<PropertyInfo, string>(2);
 
                internal RenameCanonicalFunctionPropertyTranslator()
                    : base(GetProperties()) { }
 
                private static IEnumerable<PropertyInfo> 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<T>.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<T>.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<T>.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<ParameterExpression>
        {
            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<NewExpression>
        {
            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<KeyValuePair<String, DbExpression>> recordColumns =
                    new List<KeyValuePair<string, DbExpression>>(memberCount + 1);
 
                HashSet<string> memberNames = new HashSet<string>(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<string, DbExpression>(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<NewArrayExpression>
        {
            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<ListInitExpression>
        {
            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<MemberInitExpression>
        {
            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<KeyValuePair<String, DbExpression>> recordColumns =
                    new List<KeyValuePair<string, DbExpression>>(linq.Bindings.Count + 1);
                MemberInfo[] members = new MemberInfo[linq.Bindings.Count];
 
                HashSet<string> memberNames = new HashSet<string>(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<string, DbExpression>(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<ConditionalExpression>
        {
            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<DbExpression> whenExpressions = new List<DbExpression>(1);
                whenExpressions.Add(parent.TranslateExpression(linq.Test));
                List<DbExpression> thenExpressions = new List<DbExpression>(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<System.Linq.Expressions.BinaryExpression>
        {
            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<DbExpression> whenExpressions = new List<DbExpression>(1);
                whenExpressions.Add(isNull);
                List<DbExpression> thenExpressions = new List<DbExpression>(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<System.Linq.Expressions.BinaryExpression>
        {
            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<System.Linq.Expressions.BinaryExpression>
        {
            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<TypeBinaryExpression>
        {
            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<System.Linq.Expressions.BinaryExpression>
        {
            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<UnaryExpression>
        {
            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<System.Linq.Expressions.UnaryExpression>
        {
            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
    }
}