You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			1617 lines
		
	
	
		
			76 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			1617 lines
		
	
	
		
			76 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| //---------------------------------------------------------------------
 | |
| // <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
 | |
|     }
 | |
| }
 |