File: System\Data\Objects\ELinq\ExpressionConverter.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="ExpressionConverter.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner  Microsoft
//---------------------------------------------------------------------
 
namespace System.Data.Objects.ELinq
{
    using System.Collections;
    using System.Collections.Generic;
    using System.Data.Common;
    using System.Data.Common.CommandTrees;
    using System.Data.Common.CommandTrees.ExpressionBuilder;
    using System.Data.Common.EntitySql;
    using System.Data.Common.Utils;
    using System.Data.Entity;
    using System.Data.Metadata.Edm;
    using System.Diagnostics;
    using System.Globalization;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Text;
 
    /// <summary>
    /// Class supporting conversion of LINQ expressions to EDM CQT expressions.
    /// </summary>
    internal sealed partial class ExpressionConverter
    {
        #region Fields
        private readonly Funcletizer _funcletizer;
        private readonly Perspective _perspective;
        private readonly Expression _expression;
        private readonly BindingContext _bindingContext;
        private Func<bool> _recompileRequired;
        private List<KeyValuePair<ObjectParameter, QueryParameterExpression>> _parameters;
        private Dictionary<DbExpression, Span> _spanMappings;
        private MergeOption? _mergeOption;
        private Dictionary<Type, InitializerMetadata> _initializers;
        private Span _span;
        private HashSet<ObjectQuery> _inlineEntitySqlQueries;
        private int _ignoreInclude;
        private readonly AliasGenerator _aliasGenerator = new AliasGenerator("LQ", 0);
        private readonly OrderByLifter _orderByLifter;
 
        #region Consts
        private const string s_visualBasicAssemblyFullName =
            "Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
        private static readonly Dictionary<ExpressionType, Translator> s_translators = InitializeTranslators();
 
        internal const string s_entityCollectionCountPropertyName = "Count";
        internal const string s_nullableHasValuePropertyName = "HasValue";
        internal const string s_nullableValuePropertyName = "Value";
 
        /// <summary>
        /// Gets the name of the key column appearing in ELinq GroupBy projections
        /// </summary>
        internal const string KeyColumnName = "Key";
 
        /// <summary>
        /// Gets the name of the group column appearing in ELinq CQTs (used in GroupBy expressions)
        /// </summary>
        internal const string GroupColumnName = "Group";
 
        /// <summary>
        /// Gets the name of the parent column appearing in ELinq EntityCollection projections
        /// </summary>
        internal const string EntityCollectionOwnerColumnName = "Owner";
 
        /// <summary>
        /// Gets the name of the children column appearing in ELinq EntityCollection projections
        /// </summary>
        internal const string EntityCollectionElementsColumnName = "Elements";
 
        /// <summary>
        /// The Edm namespace name, used for canonical functions
        /// </summary>
        internal const string EdmNamespaceName = "Edm";
        #endregion
 
        #region Canonical Function Names
        private const string Concat = "Concat";
        private const string IndexOf = "IndexOf";
        private const string Length = "Length";
        private const string Right = "Right";
        private const string Substring = "Substring";
        private const string ToUpper = "ToUpper";
        private const string ToLower = "ToLower";
        private const string Trim = "Trim";
        private const string LTrim = "LTrim";
        private const string RTrim = "RTrim";
        private const string Reverse = "Reverse";
        private const string BitwiseAnd = "BitwiseAnd";
        private const string BitwiseOr = "BitwiseOr";
        private const string BitwiseNot = "BitwiseNot";
        private const string BitwiseXor = "BitwiseXor";
        private const string CurrentUtcDateTime = "CurrentUtcDateTime";
        private const string CurrentDateTimeOffset = "CurrentDateTimeOffset";
        private const string CurrentDateTime = "CurrentDateTime";
        private const string Year = "Year";
        private const string Month = "Month";
        private const string Day = "Day";
        private const string Hour = "Hour";
        private const string Minute = "Minute";
        private const string Second = "Second";
        private const string Millisecond = "Millisecond";
        #endregion
 
        #region Additional Entity function names
        private const string AsUnicode = "AsUnicode";
        private const string AsNonUnicode = "AsNonUnicode";
        #endregion
        #endregion
 
        #region Constructors and static initializors
        internal ExpressionConverter(Funcletizer funcletizer, Expression expression)
        {
            EntityUtil.CheckArgumentNull(funcletizer, "funcletizer");
            EntityUtil.CheckArgumentNull(expression, "expression");
 
            // Funcletize the expression (identify subexpressions that should be evaluated
            // locally)
            _funcletizer = funcletizer;
            expression = funcletizer.Funcletize(expression, out _recompileRequired);
 
            // Normalize the expression (replace obfuscated parts of the tree with simpler nodes)
            LinqExpressionNormalizer normalizer = new LinqExpressionNormalizer();
            _expression = normalizer.Visit(expression);
 
            _perspective = funcletizer.RootContext.Perspective;
            _bindingContext = new BindingContext();
            _ignoreInclude = 0;
            _orderByLifter = new OrderByLifter(_aliasGenerator);
        }
 
        // initialize translator dictionary (which support identification of translators
        // for LINQ expression node types)
        private static Dictionary<ExpressionType, Translator> InitializeTranslators()
        {
            Dictionary<ExpressionType, Translator> translators = new Dictionary<ExpressionType, Translator>();
            foreach (Translator translator in GetTranslators())
            {
                foreach (ExpressionType nodeType in translator.NodeTypes)
                {
                    translators.Add(nodeType, translator);
                }
            }
 
            return translators;
        }
 
        private static IEnumerable<Translator> GetTranslators()
        {
            yield return new AndAlsoTranslator();
            yield return new OrElseTranslator();
            yield return new LessThanTranslator();
            yield return new LessThanOrEqualsTranslator();
            yield return new GreaterThanTranslator();
            yield return new GreaterThanOrEqualsTranslator();
            yield return new EqualsTranslator();
            yield return new NotEqualsTranslator();
            yield return new ConvertTranslator();
            yield return new ConstantTranslator();
            yield return new NotTranslator();
            yield return new MemberAccessTranslator();
            yield return new ParameterTranslator();
            yield return new MemberInitTranslator();
            yield return new NewTranslator();
            yield return new AddTranslator();
            yield return new ConditionalTranslator();
            yield return new DivideTranslator();
            yield return new ModuloTranslator();
            yield return new SubtractTranslator();
            yield return new MultiplyTranslator();
            yield return new NegateTranslator();
            yield return new UnaryPlusTranslator();
            yield return new MethodCallTranslator();
            yield return new CoalesceTranslator();
            yield return new AsTranslator();
            yield return new IsTranslator();
            yield return new QuoteTranslator();
            yield return new AndTranslator();
            yield return new OrTranslator();
            yield return new ExclusiveOrTranslator();
            yield return new ExtensionTranslator();
            yield return new NewArrayInitTranslator();
            yield return new ListInitTranslator();
            yield return new NotSupportedTranslator(
                ExpressionType.LeftShift,
                ExpressionType.RightShift,
                ExpressionType.ArrayLength,
                ExpressionType.ArrayIndex,
                ExpressionType.Invoke,
                ExpressionType.Lambda,
                ExpressionType.NewArrayBounds,
                ExpressionType.Power);
        }
        #endregion
 
        #region Properties
        private EdmItemCollection EdmItemCollection
        {
            get
            {
                return (EdmItemCollection)_funcletizer.RootContext.MetadataWorkspace.GetItemCollection(DataSpace.CSpace, true);
            }
        }
        internal DbProviderManifest ProviderManifest
        {
            get
            {
                return ((StoreItemCollection)_funcletizer.RootContext.MetadataWorkspace.GetItemCollection(DataSpace.SSpace)).StoreProviderManifest;
            }
        }
        internal System.Collections.ObjectModel.ReadOnlyCollection<KeyValuePair<ObjectParameter, QueryParameterExpression>> GetParameters()
        {
            if (null != _parameters) { return _parameters.AsReadOnly(); }
            return null;
        }
        internal MergeOption? PropagatedMergeOption { get { return _mergeOption; } }
        internal Span PropagatedSpan { get { return _span; } }
        internal Func<bool> RecompileRequired { get { return _recompileRequired; } }
        internal int IgnoreInclude { get { return _ignoreInclude; } set { _ignoreInclude = value; } }
        internal AliasGenerator AliasGenerator { get { return _aliasGenerator; } }
        #endregion
 
        #region Internal methods
        // Convert the LINQ expression to a CQT expression and (optional) Span information.
        // Span information will only be present if ObjectQuery instances that specify Spans
        // are referenced from the LINQ expression in a manner consistent with the Span combination
        // rules, otherwise the Span for the CQT expression will be null.
        internal DbExpression Convert()
        {
            DbExpression result = this.TranslateExpression(_expression);
            if (!TryGetSpan(result, out _span))
            {
                _span = null;
            }
            return result;
        }
 
        internal static bool CanFuncletizePropertyInfo(PropertyInfo propertyInfo)
        {
            return MemberAccessTranslator.CanFuncletizePropertyInfo(propertyInfo);
        }
 
        internal bool CanIncludeSpanInfo()
        {
            return (_ignoreInclude == 0);
        }
        #endregion
 
        #region Private Methods
 
        private void NotifyMergeOption(MergeOption mergeOption)
        {
            if (!this._mergeOption.HasValue)
            {
                this._mergeOption = mergeOption;
            }
        }
 
        // Requires: metadata must not be null.
        //
        // Effects: adds initializer metadata to this query context.
        // 
        // Ensures that the given initializer metadata is valid within the current converter context.
        // We do not allow two incompatible structures representing the same type within a query, e.g.,
        //
        //      outer.Join(inner, o => new Foo { X = o.ID }, i => new Foo { Y = i.ID }, ...
        //
        // since this introduces a discrepancy between the CLR (where comparisons between Foo are aware
        // of both X and Y) and in ELinq (where comparisons are based on the row structure only), resulting
        // in the following join predicates:
        //
        //      Linq: foo1 == foo2 (which presumably amounts to foo1.X == foo2.X && foo1.Y == foo2.Y
        //      ELinq: foo1.X == foo2.Y
        //
        // Similar problems occur with set operations such as Union and Concat, where one of the initialization
        // patterns may be ignored.
        //
        // This method performs an overly strict check, requiring that all initializers for a given type
        // are structurally equivalent.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2301", Justification = "metadata.ClrType is not expected to be an Embedded Interop Type.")]
        internal void ValidateInitializerMetadata(InitializerMetadata metadata)
        {
            Debug.Assert(null != metadata);
            InitializerMetadata existingMetadata;
            if (_initializers != null && _initializers.TryGetValue(metadata.ClrType, out existingMetadata))
            {
                // Verify the initializers are compatible.
                if (!metadata.Equals(existingMetadata))
                {
                    throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedHeterogeneousInitializers(
                        ExpressionConverter.DescribeClrType(metadata.ClrType)));
                }
            }
            else
            {
                // Register the metadata so that subsequent initializers for this type can be verified.
                if (_initializers == null)
                {
                    _initializers = new Dictionary<Type, InitializerMetadata>();
                }
                _initializers.Add(metadata.ClrType, metadata);
            }
        }
 
        private void AddParameter(QueryParameterExpression queryParameter)
        {
            if (null == _parameters)
            {
                _parameters = new List<KeyValuePair<ObjectParameter, QueryParameterExpression>>();
            }
            if (!_parameters.Select(p => p.Value).Contains(queryParameter))
            {
                ObjectParameter parameter = new ObjectParameter(queryParameter.ParameterReference.ParameterName, queryParameter.Type);
                _parameters.Add(new KeyValuePair<ObjectParameter, QueryParameterExpression>(parameter, queryParameter));
            }
        }
 
        private bool IsQueryRoot(Expression Expression)
        {
            //
            // An expression is the query root if it was the expression used
            // when constructing this converter.
            //
            return object.ReferenceEquals(_expression, Expression);
        }
 
        #region Span Mapping maintenance methods
 
        /// <summary>
        /// Adds a new mapping from DbExpression => Span information for the specified expression,
        /// after first ensuring that the mapping dictionary has been instantiated.
        /// </summary>
        /// <param name="expression">The expression for which Span information should be added</param>
        /// <param name="span">
        ///     The Span information, which may be <c>null</c>. 
        ///     If <c>null</c>, no attempt is made to update the dictionary of span mappings.
        /// </param>
        /// <returns>The original <paramref name="expression"/> argument, to allow <c>return AddSpanMapping(expression, span)</c> scenarios</returns>
        private DbExpression AddSpanMapping(DbExpression expression, Span span)
        {
            if (span != null && this.CanIncludeSpanInfo())
            {
                if (null == _spanMappings)
                {
                    _spanMappings = new Dictionary<DbExpression, Span>();
                }
                Span storedSpan = null;
                if (_spanMappings.TryGetValue(expression, out storedSpan))
                {
                    foreach (Span.SpanPath sp in span.SpanList)
                    {
                        storedSpan.AddSpanPath(sp);
                    }
                    _spanMappings[expression] = storedSpan;
                }
                else
                {
                    _spanMappings[expression] = span;
                }
            }
 
            return expression;
        }
 
        /// <summary>
        /// Attempts to retrieve Span information for the specified DbExpression.
        /// </summary>
        /// <param name="expression">The expression for which Span information should be retrieved.</param>
        /// <param name="span">Will contain the Span information for the specified expression if it is present in the Span mapping dictionary.</param>
        /// <returns><c>true</c> if Span information was retrieved for the specified expression and <paramref name="span"/> now contains this information; otherwise <c>false</c>.</returns>
        private bool TryGetSpan(DbExpression expression, out Span span)
        {
            if (_spanMappings != null)
            {
                return _spanMappings.TryGetValue(expression, out span);
            }
 
            span = null;
            return false;
        }
 
        /// <summary>
        /// Removes the Span mapping entry for the specified <paramref name="from"/> expression,
        /// and creates a new entry for the specified <paramref name="to"/> expression that maps
        /// to the <paramref name="from"/> expression's original Span information. If no Span
        /// information is present for the specified <paramref name="from"/> expression then no
        /// changes are made to the Span mapping dictionary.
        /// </summary>
        /// <param name="from">The expression from which to take Span information</param>
        /// <param name="to">The expression to which the Span information should be applied</param>
        private void ApplySpanMapping(DbExpression from, DbExpression to)
        {
            Span argumentSpan;
            if (TryGetSpan(from, out argumentSpan))
            {
                AddSpanMapping(to, argumentSpan);
            }
        }
 
        /// <summary>
        /// Unifies the Span information from the specified <paramref name="left"/> and <paramref name="right"/>
        /// expressions, and applies it to the specified <paramref name="to"/> expression. Unification proceeds
        /// as follows:
        /// - If neither <paramref name="left"/> nor <paramref name="right"/> have Span information, no changes are made
        /// - If one of <paramref name="left"/> or <paramref name="right"/> has Span information, that single Span information
        ///   entry is removed from the Span mapping dictionary and used to create a new entry that maps from the <paramref name="to"/>
        ///   expression to the Span information.
        /// - If both <paramref name="left"/> and <paramref name="right"/> have Span information, both entries are removed
        ///   from the Span mapping dictionary, a new Span is created that contains the union of the original Spans, and
        ///   a new entry is added to the dictionary that maps from <paramref name="to"/> expression to this new Span.
        /// </summary>
        /// <param name="left">The first expression argument</param>
        /// <param name="right">The second expression argument</param>
        /// <param name="to">The result expression</param>
        private void UnifySpanMappings(DbExpression left, DbExpression right, DbExpression to)
        {
            Span leftSpan = null;
            Span rightSpan = null;
 
            bool hasLeftSpan = TryGetSpan(left, out leftSpan);
            bool hasRightSpan = TryGetSpan(right, out rightSpan);
            if (!hasLeftSpan && !hasRightSpan)
            {
                return;
            }
 
            Debug.Assert(leftSpan != null || rightSpan != null, "Span mappings contain null?");
            AddSpanMapping(to, Span.CopyUnion(leftSpan, rightSpan));
        }
        #endregion
 
        // The following methods correspond to query builder methods on ObjectQuery
        // and MUST be called by expression translators (instead of calling the equivalent
        // CommandTree.CreateXxExpression methods) to ensure that Span information flows
        // correctly to the root of the Command Tree as it is constructed by converting
        // the LINQ expression tree. Each method correctly maintains a Span mapping (if required)
        // for its resulting expression, based on the Span mappings of its argument expression(s).
 
        private DbDistinctExpression Distinct(DbExpression argument)
        {
            DbDistinctExpression retExpr = argument.Distinct();
            ApplySpanMapping(argument, retExpr);
            return retExpr;
        }
 
        private DbExceptExpression Except(DbExpression left, DbExpression right)
        {
            DbExceptExpression retExpr = left.Except(right);
            ApplySpanMapping(left, retExpr);
            return retExpr;
        }
 
        private DbExpression Filter(DbExpressionBinding input, DbExpression predicate)
        {
            DbExpression retExpr = _orderByLifter.Filter(input, predicate);
            ApplySpanMapping(input.Expression, retExpr);
            return retExpr;
        }
 
        private DbIntersectExpression Intersect(DbExpression left, DbExpression right)
        {
            DbIntersectExpression retExpr = left.Intersect(right);
            UnifySpanMappings(left, right, retExpr);
            return retExpr;
        }
 
        private DbExpression Limit(DbExpression argument, DbExpression limit)
        {
            DbExpression retExpr = _orderByLifter.Limit(argument, limit);
            ApplySpanMapping(argument, retExpr);
            return retExpr;
        }
 
        private DbExpression OfType(DbExpression argument, TypeUsage ofType)
        {
            DbExpression retExpr = _orderByLifter.OfType(argument, ofType);
            ApplySpanMapping(argument, retExpr);
            return retExpr;
        }
 
        private DbExpression Project(DbExpressionBinding input, DbExpression projection)
        {
            DbExpression retExpr = _orderByLifter.Project(input, projection);
            // For identity projection only, the Span is preserved
            if (projection.ExpressionKind == DbExpressionKind.VariableReference &&
               ((DbVariableReferenceExpression)projection).VariableName.Equals(input.VariableName, StringComparison.Ordinal))
            {
                ApplySpanMapping(input.Expression, retExpr);
            }
            return retExpr;
        }
 
        private DbSortExpression Sort(DbExpressionBinding input, IList<DbSortClause> keys)
        {
            DbSortExpression retExpr = input.Sort(keys);
            ApplySpanMapping(input.Expression, retExpr);
            return retExpr;
        }
 
        private DbExpression Skip(DbExpressionBinding input, DbExpression skipCount)
        {
            DbExpression retExpr = _orderByLifter.Skip(input, skipCount);
            ApplySpanMapping(input.Expression, retExpr);
            return retExpr;
        }
 
        private DbUnionAllExpression UnionAll(DbExpression left, DbExpression right)
        {
            DbUnionAllExpression retExpr = left.UnionAll(right);
            UnifySpanMappings(left, right, retExpr);
            return retExpr;
        }
 
        /// <summary>
        /// Gets the target type for a CQT cast operation.
        /// </summary>
        /// <returns>Appropriate type usage, or null if this is a "no-op"</returns>
        private TypeUsage GetCastTargetType(TypeUsage fromType, Type toClrType, Type fromClrType, bool preserveCastForDateTime)
        {
            // An inlined ObjectQuery or an IOrderedQueryable expression being cast to IQueryable for use in a sequence method is a no-op.
            if(fromClrType != null &&
                fromClrType.IsGenericType && toClrType.IsGenericType &&                
                (fromClrType.GetGenericTypeDefinition() == typeof(ObjectQuery<>) || fromClrType.GetGenericTypeDefinition() == typeof(IOrderedQueryable<>)) &&
                (toClrType.GetGenericTypeDefinition() == typeof(IQueryable<>) || toClrType.GetGenericTypeDefinition() == typeof(IOrderedQueryable<>)) &&
                fromClrType.GetGenericArguments()[0] == toClrType.GetGenericArguments()[0])
            {
                return null;
            }
 
            // If the types are the same or the fromType is assignable to toType, return null
            // (indicating no cast is required)
            TypeUsage toType;
            if (TryGetValueLayerType(toClrType, out toType) && CanOmitCast(fromType, toType, preserveCastForDateTime))
            {
                return null;
            }
 
            // Check that the cast is supported and adjust the target type as necessary.
            toType = ValidateAndAdjustCastTypes(toType, fromType, toClrType, fromClrType);
 
            return toType;
        }
 
        /// <summary>
        /// Check that the given cast specification is supported and if necessary adjust target type (for instance
        /// add precision and scale for Integral -> Decimal casts)
        /// </summary>
        private static TypeUsage ValidateAndAdjustCastTypes(TypeUsage toType, TypeUsage fromType, Type toClrType, Type fromClrType)
        {
            // only support primitives if real casting is involved
            if (toType == null || !TypeSemantics.IsScalarType(toType) || !TypeSemantics.IsScalarType(fromType))
            {
                throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedCast(DescribeClrType(fromClrType), DescribeClrType(toClrType)));
            }
 
            PrimitiveTypeKind fromTypeKind = Helper.AsPrimitive(fromType.EdmType).PrimitiveTypeKind;
            PrimitiveTypeKind toTypeKind = Helper.AsPrimitive(toType.EdmType).PrimitiveTypeKind;
 
            if (toTypeKind == PrimitiveTypeKind.Decimal)
            {
                // Can't figure out the right precision and scale for decimal, so only accept integer types
                switch (fromTypeKind)
                {
                    case PrimitiveTypeKind.Byte:
                    case PrimitiveTypeKind.Int16:
                    case PrimitiveTypeKind.Int32:
                    case PrimitiveTypeKind.Int64:
                    case PrimitiveTypeKind.SByte:
                        // adjust precision and scale to ensure sufficient width
                        toType = TypeUsage.CreateDecimalTypeUsage((PrimitiveType)toType.EdmType, 19, 0);
                        break;
                    default:
                        throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedCastToDecimal);
                }
            }
 
            return toType;
        }
 
        /// <summary>
        /// Determines if an instance of fromType can be assigned to an instance of toType using
        /// CLR semantics. in case of primitive type, it must rely on identity since unboxing primitive requires 
        /// exact match. for nominal types, rely on subtyping.
        /// </summary>
        private static bool CanOmitCast(TypeUsage fromType, TypeUsage toType, bool preserveCastForDateTime)
        {
            bool isPrimitiveType = TypeSemantics.IsPrimitiveType(fromType);
 
            //SQLBUDT #573573: This is to allow for a workaround on Katmai via explicit casting by the user.
            // The issue is that SqlServer's type Date maps to Edm.DateTime, same as SqlServer's DateTime and SmallDateTime.
            // However the conversion is not possible for all values of Date.
 
            //Note: we could also call here TypeSemantics.IsPrimitiveType(TypeUsage type, PrimitiveTypeKind primitiveTypeKind),
            //  but that checks again whether the type is primitive
            if (isPrimitiveType && preserveCastForDateTime && ((PrimitiveType)fromType.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.DateTime)
            {
                return false;
            }
 
            if (TypeUsageEquals(fromType, toType))
            {
                return true;
            }
 
            if (isPrimitiveType)
            {
                return fromType.EdmType.EdmEquals(toType.EdmType);
            }
 
            return TypeSemantics.IsSubTypeOf(fromType, toType);
        }
 
        /// <summary>
        /// Gets the target type for an Is or As expression.
        /// </summary>
        /// <param name="fromType">Input type in model metadata.</param>
        /// <param name="toClrType">Test or return type.</param>
        /// <param name="operationType">Type of operation; used in error reporting.</param>
        /// <param name="fromClrType">Input type in CLR metadata.</param>
        /// <returns>Appropriate target type usage.</returns>
        private TypeUsage GetIsOrAsTargetType(TypeUsage fromType, ExpressionType operationType, Type toClrType, Type fromClrType)
        {
            Debug.Assert(operationType == ExpressionType.TypeAs || operationType == ExpressionType.TypeIs);
 
            // Interpret all type information
            TypeUsage toType;
            if (!this.TryGetValueLayerType(toClrType, out toType) ||
                (!TypeSemantics.IsEntityType(toType) &&
                 !TypeSemantics.IsComplexType(toType)))
            {
                throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedIsOrAs(operationType,
                    DescribeClrType(fromClrType), DescribeClrType(toClrType)));
            }
 
            return toType;
        }
 
        // requires: inlineQuery is not null and inlineQuery is Entity-SQL query
        // effects: interprets the given query as an inline query in the current expression and unites
        // the current query context with the context for the inline query. If the given query specifies
        // span information, then an entry is added to the span mapping dictionary from the CQT expression
        // that is the root of the inline query, to the span information that was present in the inline
        // query's Span property.
        private DbExpression TranslateInlineQueryOfT(ObjectQuery inlineQuery)
        {
            if (!object.ReferenceEquals(_funcletizer.RootContext, inlineQuery.QueryState.ObjectContext))
            {
                throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedDifferentContexts);
            }
 
            // Check if the inline query has been encountered so far. If so, we don't need to
            // include its parameters again. We do however need to translate it to a new
            // DbExpression instance since the expressions may be tagged with span information
            // and we don't want to mistakenly apply the directive to the wrong part of the query.
            if (null == _inlineEntitySqlQueries)
            {
                _inlineEntitySqlQueries = new HashSet<ObjectQuery>();
            }
            bool isNewInlineQuery = _inlineEntitySqlQueries.Add(inlineQuery);
            
            // The ObjectQuery should be Entity-SQL-based at this point. All other query types are currently
            // inlined.
            EntitySqlQueryState esqlState = (EntitySqlQueryState)inlineQuery.QueryState;
 
            // We will produce the translated expression by parsing the Entity-SQL query text.
            DbExpression resultExpression = null;
 
            // If we are not converting a compiled query, or the referenced Entity-SQL ObjectQuery
            // does not have parameters (and so no parameter references can be in the parsed tree)
            // then the Entity-SQL can be parsed directly using the conversion command tree.
            ObjectParameterCollection objectParameters = inlineQuery.QueryState.Parameters;
            if (!_funcletizer.IsCompiledQuery ||
                objectParameters == null ||
                objectParameters.Count == 0)
            {
                // Add parameters if they exist and we haven't yet encountered this inline query.
                if (isNewInlineQuery && objectParameters != null)
                {
                    // Copy the parameters into the aggregated parameter collection - this will result
                    // in an exception if any duplicate parameter names are encountered.
                    if (this._parameters == null)
                    {
                        this._parameters = new List<KeyValuePair<ObjectParameter, QueryParameterExpression>>();
                    }
                    foreach (ObjectParameter prm in inlineQuery.QueryState.Parameters)
                    {
                        this._parameters.Add(new KeyValuePair<ObjectParameter, QueryParameterExpression>(prm.ShallowCopy(), null));
                    }
                }
 
                resultExpression = esqlState.Parse();
            }
            else
            {
                // We are converting a compiled query and parameters are present on the referenced ObjectQuery.
                // The set of parameters available to a compiled query is fixed (so that adding/removing parameters
                // to/from a referenced ObjectQuery does not invalidate the compiled query's execution plan), so the
                // referenced ObjectQuery will be fully inlined by replacing each parameter reference with a
                // DbConstantExpression containing the value of the referenced parameter.
                resultExpression = esqlState.Parse();
                resultExpression = ParameterReferenceRemover.RemoveParameterReferences(resultExpression, objectParameters);
            }
 
            return resultExpression;
        }
 
        private class ParameterReferenceRemover : DefaultExpressionVisitor
        {
            internal static DbExpression RemoveParameterReferences(DbExpression expression, ObjectParameterCollection availableParameters)
            {
                ParameterReferenceRemover remover = new ParameterReferenceRemover(availableParameters);
                return remover.VisitExpression(expression);
            }
 
            private readonly ObjectParameterCollection objectParameters;
            private ParameterReferenceRemover(ObjectParameterCollection availableParams)
                : base()
            {
                Debug.Assert(availableParams != null, "Parameter collection cannot be null");
 
                this.objectParameters = availableParams;
            }
 
            public override DbExpression Visit(DbParameterReferenceExpression expression)
            {
                if (this.objectParameters.Contains(expression.ParameterName))
                {
                    // A DbNullExpression is required for null values; DbConstantExpression otherwise.
                    ObjectParameter objParam = objectParameters[expression.ParameterName];
                    if (null == objParam.Value)
                    {
                        return DbExpressionBuilder.Null(expression.ResultType);
                    }
                    else
                    {
                        // This will throw if the value is incompatible with the result type.
                        return DbExpressionBuilder.Constant(expression.ResultType, objParam.Value);
                    }
                }
                return expression;
            }
        }
 
        // creates a CQT cast expression given the source and target CLR type
        private DbExpression CreateCastExpression(DbExpression source, Type toClrType, Type fromClrType)
        {
            // see if the source can be normalized as a set
            DbExpression setSource = NormalizeSetSource(source);
            if (!Object.ReferenceEquals(source, setSource))
            {
                // if the resulting cast is a no-op (no either kind is supported
                // for set sources), yield the source
                if (null == GetCastTargetType(setSource.ResultType, toClrType, fromClrType, true))
                {
                    return source;
                }
            }
 
            // try to find the appropriate target target for the cast
            TypeUsage toType = GetCastTargetType(source.ResultType, toClrType, fromClrType, true);
            if (null == toType)
            {
                // null indicates a no-op cast (from the perspective of the model)
                return source;
            }
 
            return source.CastTo(toType);
        }
 
        // Utility translator method for lambda expressions. Given a lambda expression and its translated
        // inputs, translates the lambda expression, assuming the input is a collection
        private DbExpression TranslateLambda(LambdaExpression lambda, DbExpression input, out DbExpressionBinding binding)
        {
            input = NormalizeSetSource(input);
 
            // create binding context for this lambda expression
            binding = input.BindAs(_aliasGenerator.Next());
 
            return TranslateLambda(lambda, binding.Variable);
        }
 
        // Utility translator method for lambda expressions. Given a lambda expression and its translated
        // inputs, translates the lambda expression, assuming the input is a collection
        private DbExpression TranslateLambda(LambdaExpression lambda, DbExpression input, string bindingName, out DbExpressionBinding binding)
        {
            input = NormalizeSetSource(input);
 
            // create binding context for this lambda expression
            binding = input.BindAs(bindingName);
 
            return TranslateLambda(lambda, binding.Variable);
        }
 
        // Utility translator method for lambda expressions that are part of group by. Given a lambda expression and its translated
        // inputs, translates the lambda expression, assuming the input needs to be used as a grouping input
        private DbExpression TranslateLambda(LambdaExpression lambda, DbExpression input, out DbGroupExpressionBinding binding)
        {
            input = NormalizeSetSource(input);
 
            // create binding context for this lambda expression
            string alias = _aliasGenerator.Next();
            binding = input.GroupBindAs(alias, string.Format(CultureInfo.InvariantCulture, "Group{0}", alias));
 
            return TranslateLambda(lambda, binding.Variable);
        }
 
        // Utility translator method for lambda expressions. Given a lambda expression and its translated
        // inputs, translates the lambda expression
        private DbExpression TranslateLambda(LambdaExpression lambda, DbExpression input)
        {
            Binding scopeBinding = new Binding(lambda.Parameters[0], input);
 
            // push the binding scope
            _bindingContext.PushBindingScope(scopeBinding);
 
            // translate expression within this binding scope
#if DEBUG
            int preValue = _ignoreInclude;
#endif
            _ignoreInclude++;
            DbExpression result = TranslateExpression(lambda.Body);
            _ignoreInclude--;
#if DEBUG
            Debug.Assert(preValue == _ignoreInclude);
#endif
 
            // pop binding scope
            _bindingContext.PopBindingScope();
 
            return result;
        }
 
        // effects: unwraps any "structured" set sources such as IGrouping instances
        // (which acts as both a set and a structure containing a property)
        private DbExpression NormalizeSetSource(DbExpression input)
        {
            Debug.Assert(null != input);
 
            // If input looks like "select x from (...) as x", rewrite it as "(...)".
            // If input has span information attached to to it then leave it as is, otherwise 
            // span info will be lost.
            Span span;
            if (input.ExpressionKind == DbExpressionKind.Project && !TryGetSpan(input, out span))
            {
                var project = (DbProjectExpression)input;
                if (project.Projection == project.Input.Variable)
                {
                    input = project.Input.Expression;
                }
            }
 
            // determine if the lambda input is an IGrouping or EntityCollection that needs to be unwrapped
            InitializerMetadata initializerMetadata;
            if (InitializerMetadata.TryGetInitializerMetadata(input.ResultType, out initializerMetadata))
            {
                if (initializerMetadata.Kind == InitializerMetadataKind.Grouping)
                {
                    // for group by, redirect the binding to the group (rather than the property)
                    input = input.Property(ExpressionConverter.GroupColumnName);
                }
                else if (initializerMetadata.Kind == InitializerMetadataKind.EntityCollection)
                {
                    // for entity collection, redirect the binding to the children
                    input = input.Property(ExpressionConverter.EntityCollectionElementsColumnName);
                }
            }
            return input;
        }
 
        // Given a method call expression, returns the given lambda argument (unwrapping quote or closure references where 
        // necessary)
        private LambdaExpression GetLambdaExpression(MethodCallExpression callExpression, int argumentOrdinal)
        {
            Expression argument = callExpression.Arguments[argumentOrdinal];
            return (LambdaExpression)GetLambdaExpression(argument);
        }
 
        private Expression GetLambdaExpression(Expression argument)
        {
            if (ExpressionType.Lambda == argument.NodeType)
            {
                return argument;
            }
            else if (ExpressionType.Quote == argument.NodeType)
            {
                return GetLambdaExpression(((UnaryExpression)argument).Operand);
            }
 
            throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UnexpectedLinqLambdaExpressionFormat);
        }
 
        // Translate a LINQ expression acting as a set input to a CQT expression
        private DbExpression TranslateSet(Expression linq)
        {
            return NormalizeSetSource(TranslateExpression(linq));
        }
 
        // Translate a LINQ expression to a CQT expression.
        private DbExpression TranslateExpression(Expression linq)
        {
            Debug.Assert(null != linq);
 
            DbExpression result;
            if (!_bindingContext.TryGetBoundExpression(linq, out result))
            {
                // translate to a CQT expression
                Translator translator;
                if (s_translators.TryGetValue(linq.NodeType, out translator))
                {
                    result = translator.Translate(this, linq);
                }
                else
                {
                    throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UnknownLinqNodeType, -1,
                        linq.NodeType.ToString());
                }
            }
            return result;
        }
 
        // Cast expression to align types between CQT and eLINQ
        private DbExpression AlignTypes(DbExpression cqt, Type toClrType)
        {
            Type fromClrType = null; // not used in this code path
            TypeUsage toType = GetCastTargetType(cqt.ResultType, toClrType, fromClrType, false);
            if (null != toType)
            {
                return cqt.CastTo(toType);
            }
            else
            {
                return cqt;
            }
        }
 
        // Determines whether the given type is supported for materialization
        private void CheckInitializerType(Type type)
        {
            // nominal types are not supported
            TypeUsage typeUsage;
            if (_funcletizer.RootContext.Perspective.TryGetType(type, out typeUsage))
            {
                BuiltInTypeKind typeKind = typeUsage.EdmType.BuiltInTypeKind;
                if (BuiltInTypeKind.EntityType == typeKind ||
                    BuiltInTypeKind.ComplexType == typeKind)
                {
                    throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedNominalType(
                        typeUsage.EdmType.FullName));
                }
            }
 
            // types implementing IEnumerable are not supported
            if (TypeSystem.IsSequenceType(type))
            {
                throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedEnumerableType(
                    DescribeClrType(type)));
            }
        }
 
 
        // requires: Left and right are non-null.
        // effects: Determines if the given types are equivalent, ignoring facets. In
        // the case of primitive types, consider types equivalent if their kinds are 
        // equivalent.
        // comments: This method is useful in cases where the type facets or specific
        // store primitive type are not reliably known, e.g. when the EDM type is determined 
        // from the CLR type
        private static bool TypeUsageEquals(TypeUsage left, TypeUsage right)
        {
            Debug.Assert(null != left);
            Debug.Assert(null != right);
            if (left.EdmType.EdmEquals(right.EdmType)) { return true; }
 
            // compare element types for collection
            if (BuiltInTypeKind.CollectionType == left.EdmType.BuiltInTypeKind &&
                BuiltInTypeKind.CollectionType == right.EdmType.BuiltInTypeKind)
            {
                return TypeUsageEquals(
                    ((CollectionType)left.EdmType).TypeUsage,
                    ((CollectionType)right.EdmType).TypeUsage);
            }
 
            // special case for primitive types
            if (BuiltInTypeKind.PrimitiveType == left.EdmType.BuiltInTypeKind &&
                BuiltInTypeKind.PrimitiveType == right.EdmType.BuiltInTypeKind)
            {
                // since LINQ expressions cannot indicate model types directly, we must
                // consider types equivalent if they match on the given CLR equivalent
                // types (consider the Xml and String primitive types)
                return ((PrimitiveType)left.EdmType).ClrEquivalentType.Equals(
                    ((PrimitiveType)right.EdmType).ClrEquivalentType);
            }
 
            return false;
        }
 
        private TypeUsage GetValueLayerType(Type linqType)
        {
            TypeUsage type;
            if (!TryGetValueLayerType(linqType, out type))
            {
                throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedType(linqType));
            }
            return type;
        }
 
        // Determine C-Space equivalent type for linqType
        private bool TryGetValueLayerType(Type linqType, out TypeUsage type)
        {
            // Remove nullable
            Type nonNullableType = TypeSystem.GetNonNullableType(linqType);
 
            // Enum types are only supported for EDM V3 and higher, do not force loading
            // enum types for previous versions of EDM
            if (nonNullableType.IsEnum && this.EdmItemCollection.EdmVersion < XmlConstants.EdmVersionForV3)
            {
                nonNullableType = nonNullableType.GetEnumUnderlyingType();
            }
 
            // See if this is a primitive type
            PrimitiveTypeKind primitiveTypeKind;
            if (ClrProviderManifest.Instance.TryGetPrimitiveTypeKind(nonNullableType, out primitiveTypeKind))
            {
                type = EdmProviderManifest.Instance.GetCanonicalModelTypeUsage(primitiveTypeKind);
                return true;
            }
 
            // See if this is a collection type (if so, recursively resolve)
            Type elementType = TypeSystem.GetElementType(nonNullableType);
            if (elementType != nonNullableType)
            {
                TypeUsage elementTypeUsage;
                if (TryGetValueLayerType(elementType, out elementTypeUsage))
                {
                    type = TypeHelpers.CreateCollectionTypeUsage(elementTypeUsage);
                    return true;
                }
            }
 
            // Ensure the metadata for this object type is loaded
            _perspective.MetadataWorkspace.ImplicitLoadAssemblyForType(linqType, null);
 
            if(!_perspective.TryGetTypeByName(nonNullableType.FullName, false, out type))
            {
                // If the user is casting to a type that is not a model type or a primitive type it can be a cast to an enum that
                // is not in the model. In that case we use the underlying enum type. 
                // Note that if the underlying type is not any of the EF primitive types we will fail with and InvalidCastException.
                // This is consistent with what we would do when seeing a cast to a primitive type that is not a EF valid primitive 
                // type (e.g. ulong).
                if(nonNullableType.IsEnum && ClrProviderManifest.Instance.TryGetPrimitiveTypeKind(nonNullableType.GetEnumUnderlyingType(), out primitiveTypeKind))
                {
                    type = EdmProviderManifest.Instance.GetCanonicalModelTypeUsage(primitiveTypeKind);
                }
            }
 
            return type != null;
        }
 
        /// <summary>
        /// Utility method validating type for comparison ops (isNull, equals, etc.).
        /// Only primitive types, entity types, and simple row types (no IGrouping/EntityCollection) are
        /// supported.
        /// </summary>
        private static void VerifyTypeSupportedForComparison(Type clrType, TypeUsage edmType, Stack<EdmMember> memberPath)
        {
            // NOTE: due to bug in null handling for complex types, complex types are currently not supported
            // for comparisons (see SQL BU 543956)
            switch (edmType.EdmType.BuiltInTypeKind)
            {
                case BuiltInTypeKind.PrimitiveType:
                case BuiltInTypeKind.EnumType:
                case BuiltInTypeKind.EntityType:
                case BuiltInTypeKind.RefType:
                    return;
                case BuiltInTypeKind.RowType:
                    {
                        InitializerMetadata initializerMetadata;
                        if (!InitializerMetadata.TryGetInitializerMetadata(edmType, out initializerMetadata) ||
                            initializerMetadata.Kind == InitializerMetadataKind.ProjectionInitializer ||
                            initializerMetadata.Kind == InitializerMetadataKind.ProjectionNew)
                        {
                            VerifyRowTypeSupportedForComparison(clrType, (RowType)edmType.EdmType, memberPath);
                            return;
                        }
                        break;
                    }
                default:
                    break;
            }
            if (null == memberPath)
            {
                throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedComparison(DescribeClrType(clrType)));
            }
            else
            {
                // build up description of member path
                StringBuilder memberPathDescription = new StringBuilder();
                foreach (EdmMember member in memberPath)
                {
                    memberPathDescription.Append(Strings.ELinq_UnsupportedRowMemberComparison(member.Name));
                }
                memberPathDescription.Append(Strings.ELinq_UnsupportedRowTypeComparison(DescribeClrType(clrType)));
                throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedRowComparison(memberPathDescription.ToString()));
            }
        }
 
        private static void VerifyRowTypeSupportedForComparison(Type clrType, RowType rowType, Stack<EdmMember> memberPath)
        {
            foreach (EdmMember member in rowType.Properties)
            {
                if (null == memberPath)
                {
                    memberPath = new Stack<EdmMember>();
                }
                memberPath.Push(member);
                VerifyTypeSupportedForComparison(clrType, member.TypeUsage, memberPath);
                memberPath.Pop();
            }
        }
 
        /// <summary>
        /// Describe type for exception message.
        /// </summary>
        internal static string DescribeClrType(Type clrType)
        {
            string clrTypeName = clrType.Name;
            // Yes, this is a heuristic... just a best effort way of getting
            // a reasonable exception message
            if (IsCSharpGeneratedClass(clrTypeName, "DisplayClass") ||
                IsVBGeneratedClass(clrTypeName, "Closure"))
            {
                return Strings.ELinq_ClosureType;
            }
            if (IsCSharpGeneratedClass(clrTypeName, "AnonymousType") ||
                IsVBGeneratedClass(clrTypeName, "AnonymousType"))
            {
                return Strings.ELinq_AnonymousType;
            }
 
            string returnName = string.Empty;
            if (!String.IsNullOrEmpty(clrType.Namespace))
            {
                returnName += clrType.Namespace + ".";
            }
            returnName += clrType.Name;
            return returnName;
        }
 
        private static bool IsCSharpGeneratedClass(string typeName, string pattern)
        {
            return typeName.Contains("<>") && typeName.Contains("__") && typeName.Contains(pattern);
        }
 
        private static bool IsVBGeneratedClass(string typeName, string pattern)
        {
            return typeName.Contains("_") && typeName.Contains("$") && typeName.Contains(pattern);
        }
 
        /// <summary>
        /// Creates an implementation of IsNull. Throws exception when operand type is not supported.
        /// </summary>
        private DbExpression CreateIsNullExpression(DbExpression operand, Type operandClrType)
        {
            VerifyTypeSupportedForComparison(operandClrType, operand.ResultType, null);
            return operand.IsNull();
        }
 
        /// <summary>
        /// Creates an implementation of equals using the given pattern. Throws exception when argument types
        /// are not supported for equals comparison.
        /// </summary>
        private DbExpression CreateEqualsExpression(DbExpression left, DbExpression right, EqualsPattern pattern, Type leftClrType, Type rightClrType)
        {
            VerifyTypeSupportedForComparison(leftClrType, left.ResultType, null);
            VerifyTypeSupportedForComparison(rightClrType, right.ResultType, null);
 
            //For Ref Type comparison, check whether they refer to compatible Entity Types.
            TypeUsage leftType = left.ResultType;
            TypeUsage rightType = right.ResultType;
            if (leftType.EdmType.BuiltInTypeKind == BuiltInTypeKind.RefType && rightType.EdmType.BuiltInTypeKind == BuiltInTypeKind.RefType)
            {
                TypeUsage commonType;
                if (!TypeSemantics.TryGetCommonType(leftType, rightType, out commonType))
                {
                    RefType leftRefType = left.ResultType.EdmType as RefType;
                    RefType rightRefType = right.ResultType.EdmType as RefType;
                    throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedRefComparison(leftRefType.ElementType.FullName, rightRefType.ElementType.FullName));
                }
            }
 
            return RecursivelyRewriteEqualsExpression(left, right, pattern);
        }
        private DbExpression RecursivelyRewriteEqualsExpression(DbExpression left, DbExpression right, EqualsPattern pattern)
        {
            // check if either side is an initializer type
            RowType leftType = left.ResultType.EdmType as RowType;
            RowType rightType = left.ResultType.EdmType as RowType;
 
            if (null != leftType || null != rightType)
            {
                if ((null != leftType && null != rightType) && leftType.EdmEquals(rightType))
                {
                    DbExpression shreddedEquals = null;
                    // if the types are the same, use struct equivalence semantics
                    foreach (EdmProperty property in leftType.Properties)
                    {
                        DbPropertyExpression leftElement = left.Property(property);
                        DbPropertyExpression rightElement = right.Property(property);
                        DbExpression elementsEquals = RecursivelyRewriteEqualsExpression(
                            leftElement, rightElement, pattern);
 
                        // build up and expression
                        if (null == shreddedEquals) { shreddedEquals = elementsEquals; }
                        else { shreddedEquals = shreddedEquals.And(elementsEquals); }
                    }
                    return shreddedEquals;
                }
                else
                {
                    // if one or both sides is an initializer and the types are not the same,
                    // "equals" always evaluates to false
                    return DbExpressionBuilder.False;
                }
            }
            else
            {
                return ImplementEquality(left, right, pattern);
            }
        }
 
        // For comparisons, where the left and right side are nullable or not nullable, 
        // here are the (compositionally safe) null equality predicates:
        // -- x NOT NULL, y NULL
        // x = y AND  NOT (y IS NULL)
        // -- x NULL, y NULL
        // (x = y AND  (NOT (x IS NULL OR y IS NULL))) OR (x IS NULL AND y IS NULL)
        // -- x NOT NULL, y NOT NULL
        // x = y
        // -- x NULL, y NOT NULL
        // x = y AND  NOT (x IS NULL)
        private DbExpression ImplementEquality(DbExpression left, DbExpression right, EqualsPattern pattern)
        {
            switch (left.ExpressionKind)
            {
                case DbExpressionKind.Constant:
                    switch (right.ExpressionKind)
                    {
                        case DbExpressionKind.Constant: // constant EQ constant
                            return left.Equal(right);
                        case DbExpressionKind.Null: // null EQ constant --> false
                            return DbExpressionBuilder.False;
                        default:
                            return ImplementEqualityConstantAndUnknown((System.Data.Common.CommandTrees.DbConstantExpression)left, right, pattern);
                    }
                case DbExpressionKind.Null:
                    switch (right.ExpressionKind)
                    {
                        case DbExpressionKind.Constant: // null EQ constant --> false
                            return DbExpressionBuilder.False;
                        case DbExpressionKind.Null: // null EQ null --> true
                            return DbExpressionBuilder.True;
                        default: // null EQ right --> right IS NULL
                            return right.IsNull();
                    }
                default: // unknown
                    switch (right.ExpressionKind)
                    {
                        case DbExpressionKind.Constant:
                            return ImplementEqualityConstantAndUnknown((System.Data.Common.CommandTrees.DbConstantExpression)right, left, pattern);
                        case DbExpressionKind.Null: //  left EQ null --> left IS NULL
                            return left.IsNull();
                        default:
                            return ImplementEqualityUnknownArguments(left, right, pattern);
                    }
            }
        }
 
        // Generate an equality expression with one unknown operator and 
        private DbExpression ImplementEqualityConstantAndUnknown(
            System.Data.Common.CommandTrees.DbConstantExpression constant, DbExpression unknown, EqualsPattern pattern)
        {
            switch (pattern)
            {
                case EqualsPattern.Store:
                case EqualsPattern.PositiveNullEqualityNonComposable: // for Joins                    
                    return constant.Equal(unknown); // either both are non-null, or one is null and the predicate result is undefined
                case EqualsPattern.PositiveNullEqualityComposable:
                    if (!_funcletizer.RootContext.ContextOptions.UseCSharpNullComparisonBehavior)
                    {
                        return constant.Equal(unknown); // same as EqualsPattern.PositiveNullEqualityNonComposable
                    }
                    return constant.Equal(unknown).And(unknown.IsNull().Not()); // add more logic to avoid undefined result for true clr semantics
                default:
                    Debug.Fail("unknown pattern");
                    return null;
            }
        }
 
        // Generate an equality expression where the values of the left and right operands are completely unknown 
        private DbExpression ImplementEqualityUnknownArguments(DbExpression left, DbExpression right, EqualsPattern pattern)
        {
            switch (pattern)
            {
                case EqualsPattern.Store: // left EQ right
                    return left.Equal(right);
                case EqualsPattern.PositiveNullEqualityNonComposable: // for Joins
                    return left.Equal(right).Or(left.IsNull().And(right.IsNull()));
                case EqualsPattern.PositiveNullEqualityComposable:
                    {
                        var bothNotNull = left.Equal(right);
                        var bothNull = left.IsNull().And(right.IsNull());
                        if (!_funcletizer.RootContext.ContextOptions.UseCSharpNullComparisonBehavior)
                        {
                            return bothNotNull.Or(bothNull); // same as EqualsPattern.PositiveNullEqualityNonComposable
                        }
                        // add more logic to avoid undefined result for true clr semantics, ensuring composability
                        // (left EQ right AND NOT (left IS NULL OR right IS NULL)) OR (left IS NULL AND right IS NULL)
                        var anyOneIsNull = left.IsNull().Or(right.IsNull());
                        return (bothNotNull.And(anyOneIsNull.Not())).Or(bothNull);
                    }
                default:
                    Debug.Fail("unexpected pattern");
                    return null;
            }
        }
 
        #endregion
 
        #region Helper Methods Shared by Translators
        /// <summary>
        /// Helper method for String.StartsWith, String.EndsWith and String.Contains
        /// 
        /// object.Method(argument), where Method is one of String.StartsWith, String.EndsWith or
        /// String.Contains is translated into:
        ///     1) If argument is a constant or parameter and the provider supports escaping: 
        ///         object like ("%") + argument1 + ("%"), where argument1 is argument escaped by the provider
        ///         and ("%") are appended on the begining/end depending on whether 
        ///         insertPercentAtStart/insertPercentAtEnd are specified
        ///     2) Otherwise:
        ///           object.Method(argument) ->  defaultTranslator
        /// </summary>
        /// <param name="call"></param>
        /// <param name="insertPercentAtStart">Should '%' be inserted at the begining of the pattern</param>
        /// <param name="insertPercentAtEnd">Should '%' be inserted at the end of the pattern</param>
        /// <param name="defaultTranslator">The delegate that provides the default translation</param>
        /// <returns>The translation</returns>
        private DbExpression TranslateFunctionIntoLike(MethodCallExpression call, bool insertPercentAtStart, bool insertPercentAtEnd, Func<ExpressionConverter, MethodCallExpression, DbExpression, DbExpression, DbExpression> defaultTranslator)
        {
            char escapeChar;
            bool providerSupportsEscapingLikeArgument = this.ProviderManifest.SupportsEscapingLikeArgument(out escapeChar);
            bool useLikeTranslation = false;
            bool specifyEscape = true;
 
            Expression patternExpression = call.Arguments[0];
            Expression inputExpression = call.Object;
 
            QueryParameterExpression queryParameterExpression = patternExpression as QueryParameterExpression;
            if (providerSupportsEscapingLikeArgument && (queryParameterExpression != null))
            {
                useLikeTranslation = true;
                bool specifyEscapeDummy;
                patternExpression = queryParameterExpression.EscapeParameterForLike(input => PreparePattern(input, insertPercentAtStart, insertPercentAtEnd, out specifyEscapeDummy));
            }
 
            DbExpression translatedPatternExpression = this.TranslateExpression(patternExpression);
            DbExpression translatedInputExpression = this.TranslateExpression(inputExpression);
 
            if (providerSupportsEscapingLikeArgument && translatedPatternExpression.ExpressionKind == DbExpressionKind.Constant)
            {
                useLikeTranslation = true;
                DbConstantExpression constantExpression = (DbConstantExpression)translatedPatternExpression;
 
                string preparedValue = PreparePattern((string)constantExpression.Value, insertPercentAtStart, insertPercentAtEnd, out specifyEscape);
                Debug.Assert(preparedValue != null, "The prepared value should not be null when the input is non-null");
            
                //Note: the result type needs to be taken from the original expression, as the user may have specified Unicode/Non-Unicode
                translatedPatternExpression = DbExpressionBuilder.Constant(constantExpression.ResultType, preparedValue);
            }
 
            DbExpression result;
            if (useLikeTranslation)
            {
                if (specifyEscape)
                {
                    //DevDiv #326720: The constant expression for the escape character should not have unicode set by default
                    var escapeExpression = DbExpressionBuilder.Constant(EdmProviderManifest.Instance.GetCanonicalModelTypeUsage(PrimitiveTypeKind.String), new String(new char[] { escapeChar }));
                    result = DbExpressionBuilder.Like(translatedInputExpression, translatedPatternExpression, escapeExpression);
                }
                else
                {
                    result = DbExpressionBuilder.Like(translatedInputExpression, translatedPatternExpression);
                }
            }
            else
            {
                result = defaultTranslator(this, call, translatedPatternExpression, translatedInputExpression);
            }
 
            return result;
        }
 
        /// <summary>
        /// Prepare the given input patternValue into a pattern to be used in a LIKE expression by 
        /// first escaping it by the provider and then appending "%" and the beginging/end depending 
        /// on whether insertPercentAtStart/insertPercentAtEnd is specified.
        /// </summary>
        private string PreparePattern(string patternValue, bool insertPercentAtStart, bool insertPercentAtEnd, out bool specifyEscape)
        {
            // Dev10 #800466: The pattern value if originating from a parameter value could be null
            if (patternValue == null)
            {
                specifyEscape = false;
                return null;
            }
 
            string escapedPatternValue = this.ProviderManifest.EscapeLikeArgument(patternValue);
 
            if (escapedPatternValue == null)
            {
                throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.ProviderEscapeLikeArgumentReturnedNull);
            }
 
            specifyEscape = patternValue != escapedPatternValue;
 
            System.Text.StringBuilder patternBuilder = new System.Text.StringBuilder();
            if (insertPercentAtStart)
            {
                patternBuilder.Append("%");
            }
            patternBuilder.Append(escapedPatternValue);
            if (insertPercentAtEnd)
            {
                patternBuilder.Append("%");
            }
 
            return  patternBuilder.ToString();
        }
 
        /// <summary>
        ///  Translates the arguments into DbExpressions 
        ///   and creates a canonical function with the given functionName and these arguments
        /// </summary>
        /// <param name="functionName">Should represent a non-aggregate canonical function</param>
        /// <param name="Expression">Passed only for error handling purposes</param>
        /// <param name="linqArguments"></param>
        /// <returns></returns>
        private DbFunctionExpression TranslateIntoCanonicalFunction(string functionName, Expression Expression, params Expression[] linqArguments)
        {
            DbExpression[] translatedArguments = new DbExpression[linqArguments.Length];
            for (int i = 0; i < linqArguments.Length; i++)
            {
                translatedArguments[i] = TranslateExpression(linqArguments[i]);
            }
            return CreateCanonicalFunction(functionName, Expression, translatedArguments);
        }
 
        /// <summary>
        /// Creates a canonical function with the given name and the given arguments
        /// </summary>
        /// <param name="functionName">Should represent a non-aggregate canonical function</param>
        /// <param name="Expression">Passed only for error handling purposes</param>
        /// <param name="translatedArguments"></param>
        /// <returns></returns>
        private DbFunctionExpression CreateCanonicalFunction(string functionName, Expression Expression, params DbExpression[] translatedArguments)
        {
            List<TypeUsage> translatedArgumentTypes = new List<TypeUsage>(translatedArguments.Length);
            foreach (DbExpression translatedArgument in translatedArguments)
            {
                translatedArgumentTypes.Add(translatedArgument.ResultType);
            }
            EdmFunction function = FindCanonicalFunction(functionName, translatedArgumentTypes, false /* isGroupAggregateFunction */, Expression);
            return function.Invoke(translatedArguments);
        }
 
        /// <summary>
        /// Finds a canonical function with the given functionName and argumentTypes
        /// </summary>
        /// <param name="functionName"></param>
        /// <param name="argumentTypes"></param>
        /// <param name="isGroupAggregateFunction"></param>
        /// <param name="Expression"></param>
        /// <returns></returns>
        private EdmFunction FindCanonicalFunction(string functionName, IList<TypeUsage> argumentTypes, bool isGroupAggregateFunction, Expression Expression)
        {
            return FindFunction(EdmNamespaceName, functionName, argumentTypes, isGroupAggregateFunction, Expression);
        }
 
        /// <summary>
        /// Finds a function with the given namespaceName, functionName and argumentTypes
        /// </summary>
        /// <param name="namespaceName"></param>
        /// <param name="functionName"></param>
        /// <param name="argumentTypes"></param>
        /// <param name="isGroupAggregateFunction"></param>
        /// <param name="Expression"></param>
        /// <returns></returns>
        private EdmFunction FindFunction(string namespaceName, string functionName, IList<TypeUsage> argumentTypes, bool isGroupAggregateFunction, Expression Expression)
        {
            // find the function
            IList<EdmFunction> candidateFunctions;
            if (!_perspective.TryGetFunctionByName(namespaceName, functionName, false /* ignore case */, out candidateFunctions))
            {
                ThrowUnresolvableFunction(Expression);
            }
 
            Debug.Assert(null != candidateFunctions && candidateFunctions.Count > 0, "provider functions must not be null or empty");
 
            bool isAmbiguous;
            EdmFunction function = FunctionOverloadResolver.ResolveFunctionOverloads(candidateFunctions, argumentTypes, isGroupAggregateFunction, out isAmbiguous);
            if (isAmbiguous || null == function)
            {
                ThrowUnresolvableFunctionOverload(Expression, isAmbiguous);
            }
            return function;
        }
 
        /// <summary>
        /// Helper method for FindFunction
        /// </summary>
        /// <param name="Expression"></param>
        private static void ThrowUnresolvableFunction(Expression Expression)
        {
            if (Expression.NodeType == ExpressionType.Call)
            {
                MethodInfo methodInfo = ((MethodCallExpression)Expression).Method;
                throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableFunctionForMethod(methodInfo, methodInfo.DeclaringType));
            }
            else if (Expression.NodeType == ExpressionType.MemberAccess)
            {
                string memberName;
                Type memberType;
                MemberInfo memberInfo = TypeSystem.PropertyOrField(((MemberExpression)Expression).Member, out memberName, out memberType);
                throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableFunctionForMember(memberInfo, memberInfo.DeclaringType));
            }
            throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableFunctionForExpression(Expression.NodeType));
        }
 
        /// <summary>
        /// Helper method for FindCanonicalFunction
        /// </summary>
        /// <param name="Expression"></param>
        private static void ThrowUnresolvableFunctionOverload(Expression Expression, bool isAmbiguous)
        {
            if (Expression.NodeType == ExpressionType.Call)
            {
                MethodInfo methodInfo = ((MethodCallExpression)Expression).Method;
                if (isAmbiguous)
                {
                    throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableFunctionForMethodAmbiguousMatch(methodInfo, methodInfo.DeclaringType));
                }
                else
                {
                    throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableFunctionForMethodNotFound(methodInfo, methodInfo.DeclaringType));
                }
            }
            else if (Expression.NodeType == ExpressionType.MemberAccess)
            {
                string memberName;
                Type memberType;
                MemberInfo memberInfo = TypeSystem.PropertyOrField(((MemberExpression)Expression).Member, out memberName, out memberType);
                throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableStoreFunctionForMember(memberInfo, memberInfo.DeclaringType));
            }
            throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableStoreFunctionForExpression(Expression.NodeType));
        }
 
        private DbNewInstanceExpression CreateNewRowExpression(List<KeyValuePair<string, DbExpression>> columns, InitializerMetadata initializerMetadata)
        {
            List<DbExpression> propertyValues = new List<DbExpression>(columns.Count);
            List<EdmProperty> properties = new List<EdmProperty>(columns.Count);
            for (int i = 0; i < columns.Count; i++)
            {
                var column = columns[i];
                propertyValues.Add(column.Value);
                properties.Add(new EdmProperty(column.Key, column.Value.ResultType));
            }
            RowType rowType = new RowType(properties, initializerMetadata);
            TypeUsage typeUsage = TypeUsage.Create(rowType);
            return typeUsage.New(propertyValues);
        }
 
        #endregion
   
        #region Private enums
        // Describes different implementation pattern for equality comparisons.
        // For all patterns, if one side of the expression is a constant null, converts to an IS NULL
        // expression (or resolves to 'true' or 'false' if some constraint is known for the other side).
        // 
        // If neither side is a constant null, the semantics differ:
        //
        // (1) (left EQ right) => left and right are equal and not null, so return true.
        // (2) (left IS NULL AND right IS NULL) => Both left and right are null, so return true.
        // (3) NOT (left IS NULL OR right IS NULL) =>
        //      If only one of left or right is null, (1) evaluates to "unknown" and (2) evaluates to false. So we get "unknown" from DB which is null in C#.
        //      This is not desired as it does not help in composability. Hence, (3) is used to return false instead of "unknown" when only one of the operands is null.
        //
        // Store: (1)
        // PositiveNullEqualityNonComposable: (1) OR (2) - suitable only for Join operators, as they are not composable
        // PositiveNullEqualityComposable: (1) OR (2) AND (3)
        //
        // In the actual implementation (see ImplementEquality), optimizations exist if one or the other
        // side is known not to be null.
        private enum EqualsPattern
        {
            Store, // defer to store
            PositiveNullEqualityNonComposable, // simulate C# semantics in store, return "null" if left or right is null, but not both. Suitable for joins.
            PositiveNullEqualityComposable, // simulate C# semantics in store, always return true or false
        }
        #endregion
    }
}