Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / DataEntity / System / Data / Objects / ELinq / ExpressionConverter.cs / 2 / ExpressionConverter.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] //--------------------------------------------------------------------- using CqtExpression = System.Data.Common.CommandTrees.DbExpression; using LinqExpression = System.Linq.Expressions.Expression; using System.Linq.Expressions; using System.Collections.ObjectModel; using System.Linq; using System.Collections.Generic; using System.Data.Common.CommandTrees; using System.Data.Metadata.Edm; using System.Reflection; using System.Data.Common.EntitySql; using System.Diagnostics; using System.Data.Common; using System.Globalization; using System.Data.Common.Utils; using System.Data.Objects.DataClasses; using System.Data.Objects.Internal; using System.Collections; using System.Data.Entity; using System.Text; using System.Data.Common.CommandTrees.Internal; namespace System.Data.Objects.ELinq { ////// Class supporting conversion of LINQ expressions to EDM CQT expressions. /// internal sealed partial class ExpressionConverter { #region Fields private readonly ObjectContext _context; private readonly DbCommandTree _commandTree; private readonly TypeResolver _typeResolver; private readonly BindingContext _bindingContext; private readonly Dictionary_groupByDefaultToOptimizedTranslationMap; private readonly Dictionary _variableNameToInputExpression; private readonly Dictionary > _aggregateDefaultTranslationToOptimizedTranslationInfoMap; private readonly Expression _expression; private readonly Expression _innerExpression; private readonly HashSet _closureCandidates = new HashSet (); private readonly Stack _linqExpressionStack = new Stack (); private Dictionary _spanMappings; private MergeOption? _mergeOption; private List _closureBindings; private Dictionary _initializers; private ObjectParameterCollection _parameters; private Span _span; private const string s_visualBasicAssemblyFullName = "Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; private static readonly Dictionary s_translators = InitializeTranslators(); internal const string s_entityCollectionCountPropertyName = "Count"; internal const string s_nullableHasValuePropertyName = "HasValue"; internal const string s_nullableValuePropertyName = "Value"; /// /// Gets the name of the key column appearing in ELinq GroupBy projections /// internal const string KeyColumnName = "Key"; ////// Gets the name of the group column appearing in ELinq CQTs (used in GroupBy expressions) /// internal const string GroupColumnName = "Group"; ////// Gets the name of the parent column appearing in ELinq EntityCollection projections /// internal const string EntityCollectionOwnerColumnName = "Owner"; ////// Gets the name of the children column appearing in ELinq EntityCollection projections /// internal const string EntityCollectionElementsColumnName = "Elements"; ////// The Edm namespace name, used for canonical functions /// internal const string EdmNamespaceName = "Edm"; #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 LTrim = "LTrim"; private const string RTrim = "RTrim"; 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 #endregion #region Constructors and static initializors internal ExpressionConverter(ObjectContext objectContext, BindingContext bindingContext, DbCommandTree commandTree, Expression toConvert, ObjectParameterCollection sourceParams) { Debug.Assert(objectContext != null, "A binding context is required"); Debug.Assert(bindingContext != null, "A binding context is required"); Debug.Assert(commandTree != null, "A command tree is required"); Debug.Assert(toConvert != null, "An expression is required"); _bindingContext = bindingContext; _context = objectContext; _commandTree = commandTree; if (sourceParams != null) { foreach (ObjectParameter sourceParam in sourceParams) { this.AddParameter(sourceParam); } } // Normalize the expression _expression = NormalizeExpression(toConvert); if (_expression.NodeType == ExpressionType.Lambda) { _innerExpression = ((LambdaExpression)_expression).Body; } else { _innerExpression = _expression; } _typeResolver = new TypeResolver(_context.Perspective, StringComparer.Ordinal); _groupByDefaultToOptimizedTranslationMap = new Dictionary(); _aggregateDefaultTranslationToOptimizedTranslationInfoMap = new Dictionary >(); _variableNameToInputExpression = new Dictionary (); } // initialize translator dictionary (which support identification of translators // for LINQ expression node types) private static Dictionary InitializeTranslators() { Dictionary translators = new Dictionary (); foreach (Translator translator in GetTranslators()) { foreach (ExpressionType nodeType in translator.NodeTypes) { translators.Add(nodeType, translator); } } return translators; } private static IEnumerable 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 NotSupportedTranslator( ExpressionType.LeftShift, ExpressionType.RightShift, ExpressionType.ArrayLength, ExpressionType.ArrayIndex, ExpressionType.Invoke, ExpressionType.Lambda, ExpressionType.ListInit, ExpressionType.NewArrayInit, ExpressionType.NewArrayBounds, ExpressionType.Power); } #endregion #region Properties private EdmItemCollection EdmItemCollection { get { return (EdmItemCollection)_commandTree.MetadataWorkspace.GetItemCollection(DataSpace.CSpace, true); } } private bool IsCompiledQueryMode { get { return _bindingContext.ObjectContext != null; } } internal List ClosureBindings { get { return _closureBindings; } } internal ObjectParameterCollection Parameters { get { return _parameters; } } internal MergeOption? PropagatedMergeOption { get { return _mergeOption; } } internal Span PropagatedSpan { get { return _span; } } #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 CqtExpression Convert() { CqtExpression result = this.TranslateExpression(_expression); if (!TryGetSpan(result, out _span)) { _span = null; } return result; } internal static bool CanTranslatePropertyInfo(PropertyInfo propertyInfo) { return MemberAccessTranslator.CanTranslatePropertyInfo(propertyInfo); } #endregion #region Private Methods private LinqExpression NormalizeExpression(LinqExpression expression) { HashSet clientEvaluationCandidates = new HashSet (); LinqMaximalSubtreeNominator.Nominate(expression, clientEvaluationCandidates, ExpressionEvaluator.IsExpressionNodeClientEvaluatable); expression = LinqTreeNodeEvaluator.Evaluate(expression, clientEvaluationCandidates); // find all the nodes in the closure branches, storing them in _closureCandidates LinqMaximalSubtreeNominator.Nominate(expression, _closureCandidates, e => ExpressionEvaluator.IsExpressionNodeClientEvaluatable(e) || ExpressionEvaluator.IsExpressionNodeAClosure(e)); // normalize the expression (eliminate/rewrite compiler or CLR specific expressions). LinqExpressionNormalizer normalizer = new LinqExpressionNormalizer(); expression = normalizer.Visit(expression); return expression; } /// /// Adds the specified closure binding, optionally adding the associated /// ObjectParameter (if any) to the Parameters collection. Parameters /// have already been copied over if this method is called from /// ApplyLinqStateTo, which goes through AddClosureBindingWithoutParameter to get here. /// to copy query state from one Linq ObjectQuery to another, and so /// ClosureBinding parameters need not be added here in that case. /// /// The ClosureBinding to add to the set of ClosureBindings /// /// Specifies whether the ClosureBinding's ObjectParameter (for a ParameterBinding) /// should be added to the Parameters collection. /// private void AddClosureBinding(ClosureBinding binding, bool addParam) { Debug.Assert(null != binding, "Closure binding cannot be null"); if (_closureBindings == null) { _closureBindings = new List(); } _closureBindings.Add(binding); if (addParam && null != binding.Parameter) { AddParameter(binding.Parameter); } } /// /// Adds a new closure parameter to the query context. /// /// /// Binding to add to the context. /// internal void AddClosureBinding(ClosureBinding binding) { AddClosureBinding(binding, true); } // 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. 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(); } _initializers.Add(metadata.ClrType, metadata); } } private void AddParameter(ObjectParameter newParam) { if (_parameters == null) { _parameters = new ObjectParameterCollection(_context.Perspective); } _parameters.Add(newParam); } private bool IsQueryRoot(Expression linqExpression) { // // An expression is the query root if it was the expression used // when constructing this converter or, if the root is a LambdaExpression, // the Body of the expression used when constructing this converter. // return (object.ReferenceEquals(_expression, linqExpression) || object.ReferenceEquals(_innerExpression, linqExpression)); } #region Span Mapping maintenance methods /// /// Adds a new mapping from DbExpression => Span information for the specified expression, /// after first ensuring that the mapping dictionary has been instantiated. /// /// The expression for which Span information should be added /// /// The Span information, which may benull . /// Ifnull , no attempt is made to update the dictionary of span mappings. /// ///The original private CqtExpression AddSpanMapping(CqtExpression expression, Span span) { if (span != null) { if (null == _spanMappings) { _spanMappings = new Dictionaryargument, to allow return AddSpanMapping(expression, span) scenarios(); } _spanMappings[expression] = span; } return expression; } /// /// Attempts to retrieve Span information for the specified DbExpression. /// /// The expression for which Span information should be retrieved. /// Will contain the Span information for the specified expression if it is present in the Span mapping dictionary. ///private bool TryGetSpan(CqtExpression expression, out Span span) { if (_spanMappings != null) { return _spanMappings.TryGetValue(expression, out span); } span = null; return false; } /// true if Span information was retrieved for the specified expression andnow contains this information; otherwise false ./// Removes the Span mapping entry for the specified /// The expression from which to take Span information /// The expression to which the Span information should be applied private void ApplySpanMapping(CqtExpression from, CqtExpression to) { Span argumentSpan; if (TryGetSpan(from, out argumentSpan)) { AddSpanMapping(to, argumentSpan); } } ///expression, /// and creates a new entry for the specified expression that maps /// to the expression's original Span information. If no Span /// information is present for the specified expression then no /// changes are made to the Span mapping dictionary. /// /// Unifies the Span information from the specified /// The first expression argument /// The second expression argument /// The result expression private void UnifySpanMappings(CqtExpression left, CqtExpression right, CqtExpression 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(CqtExpression argument) { DbDistinctExpression retExpr = _commandTree.CreateDistinctExpression(argument); ApplySpanMapping(argument, retExpr); return retExpr; } private DbExceptExpression Except(CqtExpression left, CqtExpression right) { DbExceptExpression retExpr = _commandTree.CreateExceptExpression(left, right); ApplySpanMapping(left, retExpr); return retExpr; } private DbFilterExpression Filter(DbExpressionBinding input, CqtExpression predicate) { DbFilterExpression retExpr = _commandTree.CreateFilterExpression(input, predicate); ApplySpanMapping(input.Expression, retExpr); return retExpr; } private DbIntersectExpression Intersect(CqtExpression left, CqtExpression right) { DbIntersectExpression retExpr = _commandTree.CreateIntersectExpression(left, right); UnifySpanMappings(left, right, retExpr); return retExpr; } private DbLimitExpression Limit(CqtExpression argument, CqtExpression limit) { DbLimitExpression retExpr = _commandTree.CreateLimitExpression(argument, limit); ApplySpanMapping(argument, retExpr); return retExpr; } private DbOfTypeExpression OfType(CqtExpression argument, TypeUsage ofType) { DbOfTypeExpression retExpr = _commandTree.CreateOfTypeExpression(argument, ofType); ApplySpanMapping(argument, retExpr); return retExpr; } private DbProjectExpression Project(DbExpressionBinding input, CqtExpression projection) { DbProjectExpression retExpr = _commandTree.CreateProjectExpression(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, IListand /// expressions, and applies it to the specified expression. Unification proceeds /// as follows: /// - If neither nor have Span information, no changes are made /// - If one of or 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 /// expression to the Span information. /// - If both and 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 expression to this new Span. /// keys) { DbSortExpression retExpr = _commandTree.CreateSortExpression(input, keys); ApplySpanMapping(input.Expression, retExpr); return retExpr; } private DbSkipExpression Skip(DbExpressionBinding input, IList keys, CqtExpression skipCount) { DbSkipExpression retExpr = _commandTree.CreateSkipExpression(input, keys, skipCount); ApplySpanMapping(input.Expression, retExpr); return retExpr; } private DbUnionAllExpression UnionAll(CqtExpression left, CqtExpression right) { DbUnionAllExpression retExpr = _commandTree.CreateUnionAllExpression(left, right); UnifySpanMappings(left, right, retExpr); return retExpr; } /// /// Gets the target type for a CQT cast operation. /// ///Appropriate type usage, or null if this is a "no-op" private TypeUsage GetCastTargetType(TypeUsage fromType, Type toClrType, Type fromClrType, bool preserveCastForDateTime) { // 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; } ////// Check that the given cast specification is supported and if necessary adjust target type (for instance /// add precision and scale for Integral -> Decimal casts) /// private static TypeUsage ValidateAndAdjustCastTypes(TypeUsage toType, TypeUsage fromType, Type toClrType, Type fromClrType) { // only support primitives if real casting is involved if (toType == null || !(TypeSemantics.IsPrimitiveType(toType)) || !(TypeSemantics.IsPrimitiveType(fromType))) { throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedCast(DescribeClrType(fromClrType), DescribeClrType(toClrType))); } PrimitiveTypeKind fromTypeKind = ((PrimitiveType)fromType.EdmType).PrimitiveTypeKind; PrimitiveTypeKind toTypeKind = ((PrimitiveType)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; } ////// 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. /// 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); } ////// Gets the target type for an Is or As expression. /// /// Input type in model metadata. /// Test or return type. /// Type of operation; used in error reporting. /// Input type in CLR metadata. ///Appropriate target type usage. 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 // 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 CqtExpression TranslateInlineQueryOfT(ObjectQuery inlineQuery) { if (!object.ReferenceEquals(_context, inlineQuery.QueryState.ObjectContext)) { throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedDifferentContexts); } // if the propagated merge option have not been set, use the MergeOption from this inline query if (!_mergeOption.HasValue && inlineQuery.QueryState.UserSpecifiedMergeOption.HasValue) { _mergeOption = inlineQuery.QueryState.UserSpecifiedMergeOption.Value; } LinqExpression queryExpression; if (inlineQuery.QueryState.TryGetExpression(out queryExpression)) { // This inline query is backed by a LINQ expression, which can be inlined into the translation pass // (after suitable normalization). Note that no span merging is required between the Span on the // inline ObjectQuery and the Span produced from the translation of its expression because eLINQ // ObjectQuery instances cannot have a non-null Span (their Span is modeled as a method call in the // LINQ expression tree). queryExpression = NormalizeExpression(queryExpression); return TranslateExpression(queryExpression); } else { // The ObjectQuery should be Entity-SQL-based at this point 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 (!this.IsCompiledQueryMode || objectParameters == null || objectParameters.Count == 0) { // Copy the parameters into the aggregated parameter collection - this will result // in an exception if any duplicate parameter names are encountered. if (objectParameters != null) { foreach (ObjectParameter prm in inlineQuery.QueryState.Parameters) { this.AddParameter(prm.ShallowCopy()); } } resultExpression = esqlState.Parse(_commandTree); } 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. // So that the parameters are never added to the conversion command tree, the referenced ObjectQuery // is parsed using a different query command tree. DbQueryCommandTree replacedTree = new DbQueryCommandTree(_commandTree.MetadataWorkspace, _commandTree.DataSpace); replacedTree.Query = esqlState.Parse(replacedTree); replacedTree.Replace((ExpressionReplacement replacement) => { // Only parameter references are being replaced if (replacement.Current.ExpressionKind != DbExpressionKind.ParameterReference) { return; } DbParameterReferenceExpression paramRef = (DbParameterReferenceExpression)replacement.Current; if (objectParameters.Contains(paramRef.ParameterName)) { // A DbNullExpression is required for null values; DbConstantExpression otherwise. ObjectParameter objParam = objectParameters[paramRef.ParameterName]; if (null == objParam.Value) { replacement.Replacement = replacement.Current.CommandTree.CreateNullExpression(replacement.Current.ResultType); } else { // This will throw if the value is incompatible with the result type. replacement.Replacement = replacement.Current.CommandTree.CreateConstantExpression(objParam.Value, replacement.Current.ResultType); } } } ); resultExpression = _commandTree.Import(replacedTree.Query); } // The Span produced by the converted DbExpression is any Span that was assigned to the referened Entity-SQL ObjectQuery. return AddSpanMapping(resultExpression, inlineQuery.QueryState.Span); } } // creates a CQT cast expression given the source and target CLR type private CqtExpression CreateCastExpression(CqtExpression source, Type toClrType, Type fromClrType) { // see if the source can be normalized as a set CqtExpression 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 _commandTree.CreateCastExpression(source, 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 CqtExpression TranslateLambda(LambdaExpression lambda, CqtExpression input, out DbExpressionBinding binding) { input = NormalizeSetSource(input); // create binding context for this lambda expression binding = _commandTree.CreateExpressionBinding(input); // figure out the binding variable DbVariableReferenceExpression bindingVariable = binding.Variable; _variableNameToInputExpression.Add(bindingVariable.VariableName, input); return TranslateLambda(lambda, bindingVariable); } // 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 CqtExpression TranslateLambda(LambdaExpression lambda, CqtExpression input, out DbGroupExpressionBinding binding) { input = NormalizeSetSource(input); // create binding context for this lambda expression binding = _commandTree.CreateGroupExpressionBinding(input); // figure out the binding variable DbVariableReferenceExpression bindingVariable = binding.Variable; return TranslateLambda(lambda, bindingVariable); } // Utility translator method for lambda expressions. Given a lambda expression and its translated // inputs, translates the lambda expression private CqtExpression TranslateLambda(LambdaExpression lambda, CqtExpression input) { Binding scopeBinding = new Binding(lambda.Parameters[0], input); // push the binding scope _bindingContext.PushBindingScope(scopeBinding); // translate expression within this binding scope CqtExpression result = TranslateExpression(lambda.Body); // 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 CqtExpression NormalizeSetSource(CqtExpression input) { Debug.Assert(null != input); // 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 = _commandTree.CreatePropertyExpression(ExpressionConverter.GroupColumnName, input); } else if (initializerMetadata.Kind == InitializerMetadataKind.EntityCollection) { // for entity collection, redirect the binding to the children input = _commandTree.CreatePropertyExpression(ExpressionConverter.EntityCollectionElementsColumnName, input); } } 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) { LinqExpression argument = callExpression.Arguments[argumentOrdinal]; return (LambdaExpression)GetLambdaExpression(argument); } private LinqExpression GetLambdaExpression(LinqExpression argument) { if (ExpressionType.Lambda == argument.NodeType) { return argument; } else if (ExpressionType.Quote == argument.NodeType) { return GetLambdaExpression(((System.Linq.Expressions.UnaryExpression)argument).Operand); } // see if this can be computed as a closure binding (resolving to an expression) ClosureBinding binding; TypeUsage typeUsage; bool allowLambda = true; if (ClosureBinding.TryCreateClosureBinding(argument, _context.Perspective, allowLambda, _closureCandidates, out binding, out typeUsage) && null != binding.Expression) { AddClosureBinding(binding); return GetLambdaExpression(binding.Expression); } throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UnexpectedLinqLambdaExpressionFormat); } // Translate a LINQ expression acting as a set input to a CQT expression private CqtExpression TranslateSet(LinqExpression linq) { return NormalizeSetSource(TranslateExpression(linq)); } // If the given input source is a GroupBy, tries to produce an optimized translation // The default translation is to 'manually' do group by, the optimized is translating into a // DbGroupByExpression private bool TryRewrite(CqtExpression source, DbExpressionBinding sourceBinding, CqtExpression lambda, out CqtExpression result) { result = null; DbGroupByTemplate optimizedTranslationTemplate; if (_groupByDefaultToOptimizedTranslationMap.TryGetValue(source, out optimizedTranslationTemplate)) { //Create the group by expression DbGroupByExpression optimizedTranslation = _commandTree.CreateGroupByExpression(optimizedTranslationTemplate.Input, optimizedTranslationTemplate.GroupKeys, optimizedTranslationTemplate.Aggregates); DbExpressionBinding optimizedSourceBinding = _commandTree.CreateExpressionBinding(optimizedTranslation); CqtExpression rewrittenLambda; if (GroupByExpressionRewriter.TryRewrite(this, lambda, sourceBinding.VariableName, optimizedSourceBinding.Variable, optimizedTranslationTemplate, out rewrittenLambda)) { result = Project(optimizedSourceBinding, rewrittenLambda); return true; } } return false; } // Translate a LINQ expression to a CQT expression. private CqtExpression TranslateExpression(LinqExpression linq) { Debug.Assert(null != linq); if (_linqExpressionStack.Contains(linq)) { throw EntityUtil.InvalidOperation(Strings.ELinq_CycleDetected); } _linqExpressionStack.Push(linq); try { ClosureBinding binding; TypeUsage bindingType; bool allowLambda = false; CqtExpression result; if (_bindingContext.TryGetBoundExpression(linq, out result)) { // see if the LINQ expression has been bound to some CQT expression already // (e.g. parameter expressions and compiled query parameters) return result; } else if (ClosureBinding.TryCreateClosureBinding(linq, _context.Perspective, allowLambda, _closureCandidates, out binding, out bindingType)) { // see if the expression is a closure binding if (!IsCompiledQueryMode) { AddClosureBinding(binding); } if (null != binding.Parameter) { if (IsCompiledQueryMode) { // pretend it was a constant expression that we found ConstantExpression constant = Expression.Constant(binding.Parameter.Value, binding.Parameter.ParameterType); return TranslateExpression(constant); } else { // Note that parameters are independently added to the command tree, not just // the query context. This is because a CQT expression tree is invalid if // its parameters are not known, and a valid tree is required (in addition to // the query context) to construct an ObjectQueryinstance. _commandTree.AddParameter(binding.Parameter.Name, bindingType); // return parameter taking the closure value return _commandTree.CreateParameterReferenceExpression(binding.Parameter.Name); } } else { Debug.Assert(null != binding.Query, "binding must be either to a query, expression (handled by TranslateLambda), or a parameter"); return TranslateInlineQueryOfT(binding.Query); } } ObjectQuery queryOfT; if (ExpressionEvaluator.TryEvaluateRootQuery(_bindingContext, linq, out queryOfT)) { return TranslateInlineQueryOfT(queryOfT); } // translate to a CQT expression Translator translator; if (s_translators.TryGetValue(linq.NodeType, out translator)) { return translator.Translate(this, linq); } else { throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UnknownLinqNodeType, -1, linq.NodeType.ToString()); } } finally { _linqExpressionStack.Pop(); } } // Cast expression to align types between CQT and eLINQ private CqtExpression AlignTypes(CqtExpression cqt, Type toClrType) { Type fromClrType = null; // not used in this code path TypeUsage toType = GetCastTargetType(cqt.ResultType, toClrType, fromClrType, false); if (null != toType) { return _commandTree.CreateCastExpression(cqt, 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 (_context.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) { // Ensure the metadata for this object type is loaded _commandTree.MetadataWorkspace.LoadAssemblyForType(linqType, System.Reflection.Assembly.GetCallingAssembly()); // Remove nullable Type nonNullableType = TypeSystem.GetNonNullableType(linqType); // 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; } } // Retrieve type from map return _context.Perspective.TryGetTypeByName( nonNullableType.FullName, false, // ignoreCase out type); } /// /// Utility method validating type for comparison ops (isNull, equals, etc.). /// Only primitive types, entity types, and simple row types (no IGrouping/EntityCollection) are /// supported. /// private static void VerifyTypeSupportedForComparison(Type clrType, TypeUsage edmType, StackmemberPath) { // 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.EntityType: 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), Strings.ELinq_PrimitiveTypesSample)); } 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(), Strings.ELinq_PrimitiveTypesSample)); } } private static void VerifyRowTypeSupportedForComparison(Type clrType, RowType rowType, Stack memberPath) { foreach (EdmMember member in rowType.Properties) { if (null == memberPath) { memberPath = new Stack (); } memberPath.Push(member); VerifyTypeSupportedForComparison(clrType, member.TypeUsage, memberPath); memberPath.Pop(); } } /// /// Describe type for exception message. /// 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); } ////// Creates an implementation of IsNull. Throws exception when operand type is not supported. /// private CqtExpression CreateIsNullExpression(CqtExpression operand, Type operandClrType) { VerifyTypeSupportedForComparison(operandClrType, operand.ResultType, null); return _commandTree.CreateIsNullExpression(operand); } ////// Creates an implementation of equals using the given pattern. Throws exception when argument types /// are not supported for equals comparison. /// private CqtExpression CreateEqualsExpression(CqtExpression left, CqtExpression right, EqualsPattern pattern, Type leftClrType, Type rightClrType) { VerifyTypeSupportedForComparison(leftClrType, left.ResultType, null); VerifyTypeSupportedForComparison(rightClrType, right.ResultType, null); return RecursivelyRewriteEqualsExpression(left, right, pattern); } private CqtExpression RecursivelyRewriteEqualsExpression(CqtExpression left, CqtExpression 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)) { CqtExpression shreddedEquals = null; // if the types are the same, use struct equivalence semantics foreach (EdmProperty property in leftType.Properties) { DbPropertyExpression leftElement = _commandTree.CreatePropertyExpression( property, left); DbPropertyExpression rightElement = _commandTree.CreatePropertyExpression( property, right); CqtExpression elementsEquals = RecursivelyRewriteEqualsExpression( leftElement, rightElement, pattern); // build up and expression if (null == shreddedEquals) { shreddedEquals = elementsEquals; } else { shreddedEquals = _commandTree.CreateAndExpression(shreddedEquals, 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 _commandTree.CreateFalseExpression(); } } 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 CqtExpression ImplementEquality(CqtExpression left, CqtExpression right, EqualsPattern pattern) { switch (left.ExpressionKind) { case DbExpressionKind.Constant: switch (right.ExpressionKind) { case DbExpressionKind.Constant: // constant EQ constant return _commandTree.CreateEqualsExpression(left, right); case DbExpressionKind.Null: // null EQ constant --> false return _commandTree.CreateFalseExpression(); 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 _commandTree.CreateFalseExpression(); case DbExpressionKind.Null: // null EQ null --> true return _commandTree.CreateTrueExpression(); default: // null EQ right --> right IS NULL return _commandTree.CreateIsNullExpression(right); } 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 _commandTree.CreateIsNullExpression(left); default: return ImplementEqualityUnknownArguments(left, right, pattern); } } } // Generate an equality expression with one unknown operator and private CqtExpression ImplementEqualityConstantAndUnknown( System.Data.Common.CommandTrees.DbConstantExpression constant, CqtExpression unknown, EqualsPattern pattern) { switch (pattern) { case EqualsPattern.Store: case EqualsPattern.PositiveNullEquality: // either both are non-null, or one is null and the predicate result is undefined return _commandTree.CreateEqualsExpression(constant, unknown); default: Debug.Fail("unknown pattern"); return null; } } // Generate an equality expression where the values of the left and right operands are completely unknown private CqtExpression ImplementEqualityUnknownArguments(CqtExpression left, CqtExpression right, EqualsPattern pattern) { switch (pattern) { case EqualsPattern.Store: // left EQ right return _commandTree.CreateEqualsExpression(left, right); case EqualsPattern.PositiveNullEquality: // left EQ right OR (left IS NULL AND right IS NULL) { CqtExpression leftIsNull = _commandTree.CreateIsNullExpression(left); CqtExpression rightIsNull = _commandTree.CreateIsNullExpression(right); CqtExpression equals = _commandTree.CreateEqualsExpression(left, right); return _commandTree.CreateOrExpression(equals, _commandTree.CreateAndExpression(leftIsNull, rightIsNull)); } default: Debug.Fail("unexpected pattern"); return null; } } #endregion #region Helper Methods Shared by Translators ////// Translates the arguments into CqtExpressions /// and creates a canonical function with the given functionName and these arguments /// /// Should represent a non-aggregate canonical function /// Passed only for error handling purposes /// ///private DbFunctionExpression TranslateIntoCanonicalFunction(string functionName, LinqExpression linqExpression, params LinqExpression[] linqArguments) { DbExpression[] translatedArguments = new DbExpression[linqArguments.Length]; for (int i = 0; i < linqArguments.Length; i++) { translatedArguments[i] = TranslateExpression(linqArguments[i]); } return CreateCanonicalFunction(functionName, linqExpression, translatedArguments); } /// /// Creates a canonical function with the given name and the given arguments /// /// Should represent a non-aggregate canonical function /// Passed only for error handling purposes /// ///private DbFunctionExpression CreateCanonicalFunction(string functionName, LinqExpression linqExpression, params CqtExpression[] translatedArguments) { List translatedArgumentTypes = new List (translatedArguments.Length); foreach (CqtExpression translatedArgument in translatedArguments) { translatedArgumentTypes.Add(translatedArgument.ResultType); } EdmFunction function = FindCanonicalFunction(functionName, translatedArgumentTypes, false /* isGroupAggregateFunction */, linqExpression); return _commandTree.CreateFunctionExpression(function, translatedArguments); } /// /// Finds a canonical function wiht the given functionName and argumentTypes /// /// /// /// /// ///private EdmFunction FindCanonicalFunction(string functionName, IList argumentTypes, bool isGroupAggregateFunction, LinqExpression linqExpression) { // find the function IList functions; if (!_typeResolver.TryGetFunctionFromMetadata(functionName, EdmNamespaceName, true /*ignoreCase*/, out functions)) { ThrowUnresolvableCanonicalFunction(linqExpression); } Debug.Assert(null != functions && functions.Count > 0, "provider functions must not be null or empty"); bool isAmbiguous; EdmFunction function = TypeResolver.ResolveFunctionOverloads(functions, argumentTypes, isGroupAggregateFunction, out isAmbiguous); if (isAmbiguous || null == function) { ThrowUnresolvableStoreFunction(linqExpression); } return function; } /// /// Helper method for FindCanonicalFunction /// /// private static void ThrowUnresolvableCanonicalFunction(LinqExpression linqExpression) { if (linqExpression.NodeType == ExpressionType.Call) { MethodInfo methodInfo = ((MethodCallExpression)linqExpression).Method; throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableCanonicalFunctionForMethod(methodInfo, methodInfo.DeclaringType)); } else if (linqExpression.NodeType == ExpressionType.MemberAccess) { string memberName; Type memberType; MemberInfo memberInfo = TypeSystem.PropertyOrField(((MemberExpression)linqExpression).Member, out memberName, out memberType); throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableCanonicalFunctionForMember(memberInfo, memberInfo.DeclaringType)); } throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableCanonicalFunctionForExpression(linqExpression.NodeType)); } ////// Helper method for FindCanonicalFunction /// /// private static void ThrowUnresolvableStoreFunction(LinqExpression linqExpression) { if (linqExpression.NodeType == ExpressionType.Call) { MethodInfo methodInfo = ((MethodCallExpression)linqExpression).Method; throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableStoreFunctionForMethod(methodInfo, methodInfo.DeclaringType)); } else if (linqExpression.NodeType == ExpressionType.MemberAccess) { string memberName; Type memberType; MemberInfo memberInfo = TypeSystem.PropertyOrField(((MemberExpression)linqExpression).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(linqExpression.NodeType)); } private DbNewInstanceExpression CreateNewRowExpression(List> columns, InitializerMetadata initializerMetadata) { List propertyValues = new List (columns.Count); List properties = new List (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 _commandTree.CreateNewInstanceExpression(typeUsage, propertyValues); } #endregion #region Helper Structures // Mirrors the structure of DbGroupByExpression // It is used for incrementally constructing a DbGroupByExpression // (the actual DbGroupByExpression cannot be used for this purpose because it is immutable) private class DbGroupByTemplate { private DbGroupExpressionBinding _input; private List > _groupKeys; private List > _aggregates; public DbGroupByTemplate(DbGroupExpressionBinding input) { _input = input; _groupKeys = new List >(); _aggregates = new List >(); } public List > GroupKeys { get { return _groupKeys; } } public List > Aggregates { get { return _aggregates; } } public DbGroupExpressionBinding Input { get { return _input; } } } #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: // // Store: left EQ right // NullEquality: left EQ right OR (left IS NULL AND right IS NULL) // PositiveEquality: left EQ right // // 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 PositiveNullEquality, // null == null is 'true', null == (not null) us undefined } #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] //--------------------------------------------------------------------- using CqtExpression = System.Data.Common.CommandTrees.DbExpression; using LinqExpression = System.Linq.Expressions.Expression; using System.Linq.Expressions; using System.Collections.ObjectModel; using System.Linq; using System.Collections.Generic; using System.Data.Common.CommandTrees; using System.Data.Metadata.Edm; using System.Reflection; using System.Data.Common.EntitySql; using System.Diagnostics; using System.Data.Common; using System.Globalization; using System.Data.Common.Utils; using System.Data.Objects.DataClasses; using System.Data.Objects.Internal; using System.Collections; using System.Data.Entity; using System.Text; using System.Data.Common.CommandTrees.Internal; namespace System.Data.Objects.ELinq { ////// Class supporting conversion of LINQ expressions to EDM CQT expressions. /// internal sealed partial class ExpressionConverter { #region Fields private readonly ObjectContext _context; private readonly DbCommandTree _commandTree; private readonly TypeResolver _typeResolver; private readonly BindingContext _bindingContext; private readonly Dictionary_groupByDefaultToOptimizedTranslationMap; private readonly Dictionary _variableNameToInputExpression; private readonly Dictionary > _aggregateDefaultTranslationToOptimizedTranslationInfoMap; private readonly Expression _expression; private readonly Expression _innerExpression; private readonly HashSet _closureCandidates = new HashSet (); private readonly Stack _linqExpressionStack = new Stack (); private Dictionary _spanMappings; private MergeOption? _mergeOption; private List _closureBindings; private Dictionary _initializers; private ObjectParameterCollection _parameters; private Span _span; private const string s_visualBasicAssemblyFullName = "Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; private static readonly Dictionary s_translators = InitializeTranslators(); internal const string s_entityCollectionCountPropertyName = "Count"; internal const string s_nullableHasValuePropertyName = "HasValue"; internal const string s_nullableValuePropertyName = "Value"; /// /// Gets the name of the key column appearing in ELinq GroupBy projections /// internal const string KeyColumnName = "Key"; ////// Gets the name of the group column appearing in ELinq CQTs (used in GroupBy expressions) /// internal const string GroupColumnName = "Group"; ////// Gets the name of the parent column appearing in ELinq EntityCollection projections /// internal const string EntityCollectionOwnerColumnName = "Owner"; ////// Gets the name of the children column appearing in ELinq EntityCollection projections /// internal const string EntityCollectionElementsColumnName = "Elements"; ////// The Edm namespace name, used for canonical functions /// internal const string EdmNamespaceName = "Edm"; #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 LTrim = "LTrim"; private const string RTrim = "RTrim"; 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 #endregion #region Constructors and static initializors internal ExpressionConverter(ObjectContext objectContext, BindingContext bindingContext, DbCommandTree commandTree, Expression toConvert, ObjectParameterCollection sourceParams) { Debug.Assert(objectContext != null, "A binding context is required"); Debug.Assert(bindingContext != null, "A binding context is required"); Debug.Assert(commandTree != null, "A command tree is required"); Debug.Assert(toConvert != null, "An expression is required"); _bindingContext = bindingContext; _context = objectContext; _commandTree = commandTree; if (sourceParams != null) { foreach (ObjectParameter sourceParam in sourceParams) { this.AddParameter(sourceParam); } } // Normalize the expression _expression = NormalizeExpression(toConvert); if (_expression.NodeType == ExpressionType.Lambda) { _innerExpression = ((LambdaExpression)_expression).Body; } else { _innerExpression = _expression; } _typeResolver = new TypeResolver(_context.Perspective, StringComparer.Ordinal); _groupByDefaultToOptimizedTranslationMap = new Dictionary(); _aggregateDefaultTranslationToOptimizedTranslationInfoMap = new Dictionary >(); _variableNameToInputExpression = new Dictionary (); } // initialize translator dictionary (which support identification of translators // for LINQ expression node types) private static Dictionary InitializeTranslators() { Dictionary translators = new Dictionary (); foreach (Translator translator in GetTranslators()) { foreach (ExpressionType nodeType in translator.NodeTypes) { translators.Add(nodeType, translator); } } return translators; } private static IEnumerable 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 NotSupportedTranslator( ExpressionType.LeftShift, ExpressionType.RightShift, ExpressionType.ArrayLength, ExpressionType.ArrayIndex, ExpressionType.Invoke, ExpressionType.Lambda, ExpressionType.ListInit, ExpressionType.NewArrayInit, ExpressionType.NewArrayBounds, ExpressionType.Power); } #endregion #region Properties private EdmItemCollection EdmItemCollection { get { return (EdmItemCollection)_commandTree.MetadataWorkspace.GetItemCollection(DataSpace.CSpace, true); } } private bool IsCompiledQueryMode { get { return _bindingContext.ObjectContext != null; } } internal List ClosureBindings { get { return _closureBindings; } } internal ObjectParameterCollection Parameters { get { return _parameters; } } internal MergeOption? PropagatedMergeOption { get { return _mergeOption; } } internal Span PropagatedSpan { get { return _span; } } #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 CqtExpression Convert() { CqtExpression result = this.TranslateExpression(_expression); if (!TryGetSpan(result, out _span)) { _span = null; } return result; } internal static bool CanTranslatePropertyInfo(PropertyInfo propertyInfo) { return MemberAccessTranslator.CanTranslatePropertyInfo(propertyInfo); } #endregion #region Private Methods private LinqExpression NormalizeExpression(LinqExpression expression) { HashSet clientEvaluationCandidates = new HashSet (); LinqMaximalSubtreeNominator.Nominate(expression, clientEvaluationCandidates, ExpressionEvaluator.IsExpressionNodeClientEvaluatable); expression = LinqTreeNodeEvaluator.Evaluate(expression, clientEvaluationCandidates); // find all the nodes in the closure branches, storing them in _closureCandidates LinqMaximalSubtreeNominator.Nominate(expression, _closureCandidates, e => ExpressionEvaluator.IsExpressionNodeClientEvaluatable(e) || ExpressionEvaluator.IsExpressionNodeAClosure(e)); // normalize the expression (eliminate/rewrite compiler or CLR specific expressions). LinqExpressionNormalizer normalizer = new LinqExpressionNormalizer(); expression = normalizer.Visit(expression); return expression; } /// /// Adds the specified closure binding, optionally adding the associated /// ObjectParameter (if any) to the Parameters collection. Parameters /// have already been copied over if this method is called from /// ApplyLinqStateTo, which goes through AddClosureBindingWithoutParameter to get here. /// to copy query state from one Linq ObjectQuery to another, and so /// ClosureBinding parameters need not be added here in that case. /// /// The ClosureBinding to add to the set of ClosureBindings /// /// Specifies whether the ClosureBinding's ObjectParameter (for a ParameterBinding) /// should be added to the Parameters collection. /// private void AddClosureBinding(ClosureBinding binding, bool addParam) { Debug.Assert(null != binding, "Closure binding cannot be null"); if (_closureBindings == null) { _closureBindings = new List(); } _closureBindings.Add(binding); if (addParam && null != binding.Parameter) { AddParameter(binding.Parameter); } } /// /// Adds a new closure parameter to the query context. /// /// /// Binding to add to the context. /// internal void AddClosureBinding(ClosureBinding binding) { AddClosureBinding(binding, true); } // 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. 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(); } _initializers.Add(metadata.ClrType, metadata); } } private void AddParameter(ObjectParameter newParam) { if (_parameters == null) { _parameters = new ObjectParameterCollection(_context.Perspective); } _parameters.Add(newParam); } private bool IsQueryRoot(Expression linqExpression) { // // An expression is the query root if it was the expression used // when constructing this converter or, if the root is a LambdaExpression, // the Body of the expression used when constructing this converter. // return (object.ReferenceEquals(_expression, linqExpression) || object.ReferenceEquals(_innerExpression, linqExpression)); } #region Span Mapping maintenance methods /// /// Adds a new mapping from DbExpression => Span information for the specified expression, /// after first ensuring that the mapping dictionary has been instantiated. /// /// The expression for which Span information should be added /// /// The Span information, which may benull . /// Ifnull , no attempt is made to update the dictionary of span mappings. /// ///The original private CqtExpression AddSpanMapping(CqtExpression expression, Span span) { if (span != null) { if (null == _spanMappings) { _spanMappings = new Dictionaryargument, to allow return AddSpanMapping(expression, span) scenarios(); } _spanMappings[expression] = span; } return expression; } /// /// Attempts to retrieve Span information for the specified DbExpression. /// /// The expression for which Span information should be retrieved. /// Will contain the Span information for the specified expression if it is present in the Span mapping dictionary. ///private bool TryGetSpan(CqtExpression expression, out Span span) { if (_spanMappings != null) { return _spanMappings.TryGetValue(expression, out span); } span = null; return false; } /// true if Span information was retrieved for the specified expression andnow contains this information; otherwise false ./// Removes the Span mapping entry for the specified /// The expression from which to take Span information /// The expression to which the Span information should be applied private void ApplySpanMapping(CqtExpression from, CqtExpression to) { Span argumentSpan; if (TryGetSpan(from, out argumentSpan)) { AddSpanMapping(to, argumentSpan); } } ///expression, /// and creates a new entry for the specified expression that maps /// to the expression's original Span information. If no Span /// information is present for the specified expression then no /// changes are made to the Span mapping dictionary. /// /// Unifies the Span information from the specified /// The first expression argument /// The second expression argument /// The result expression private void UnifySpanMappings(CqtExpression left, CqtExpression right, CqtExpression 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(CqtExpression argument) { DbDistinctExpression retExpr = _commandTree.CreateDistinctExpression(argument); ApplySpanMapping(argument, retExpr); return retExpr; } private DbExceptExpression Except(CqtExpression left, CqtExpression right) { DbExceptExpression retExpr = _commandTree.CreateExceptExpression(left, right); ApplySpanMapping(left, retExpr); return retExpr; } private DbFilterExpression Filter(DbExpressionBinding input, CqtExpression predicate) { DbFilterExpression retExpr = _commandTree.CreateFilterExpression(input, predicate); ApplySpanMapping(input.Expression, retExpr); return retExpr; } private DbIntersectExpression Intersect(CqtExpression left, CqtExpression right) { DbIntersectExpression retExpr = _commandTree.CreateIntersectExpression(left, right); UnifySpanMappings(left, right, retExpr); return retExpr; } private DbLimitExpression Limit(CqtExpression argument, CqtExpression limit) { DbLimitExpression retExpr = _commandTree.CreateLimitExpression(argument, limit); ApplySpanMapping(argument, retExpr); return retExpr; } private DbOfTypeExpression OfType(CqtExpression argument, TypeUsage ofType) { DbOfTypeExpression retExpr = _commandTree.CreateOfTypeExpression(argument, ofType); ApplySpanMapping(argument, retExpr); return retExpr; } private DbProjectExpression Project(DbExpressionBinding input, CqtExpression projection) { DbProjectExpression retExpr = _commandTree.CreateProjectExpression(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, IListand /// expressions, and applies it to the specified expression. Unification proceeds /// as follows: /// - If neither nor have Span information, no changes are made /// - If one of or 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 /// expression to the Span information. /// - If both and 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 expression to this new Span. /// keys) { DbSortExpression retExpr = _commandTree.CreateSortExpression(input, keys); ApplySpanMapping(input.Expression, retExpr); return retExpr; } private DbSkipExpression Skip(DbExpressionBinding input, IList keys, CqtExpression skipCount) { DbSkipExpression retExpr = _commandTree.CreateSkipExpression(input, keys, skipCount); ApplySpanMapping(input.Expression, retExpr); return retExpr; } private DbUnionAllExpression UnionAll(CqtExpression left, CqtExpression right) { DbUnionAllExpression retExpr = _commandTree.CreateUnionAllExpression(left, right); UnifySpanMappings(left, right, retExpr); return retExpr; } /// /// Gets the target type for a CQT cast operation. /// ///Appropriate type usage, or null if this is a "no-op" private TypeUsage GetCastTargetType(TypeUsage fromType, Type toClrType, Type fromClrType, bool preserveCastForDateTime) { // 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; } ////// Check that the given cast specification is supported and if necessary adjust target type (for instance /// add precision and scale for Integral -> Decimal casts) /// private static TypeUsage ValidateAndAdjustCastTypes(TypeUsage toType, TypeUsage fromType, Type toClrType, Type fromClrType) { // only support primitives if real casting is involved if (toType == null || !(TypeSemantics.IsPrimitiveType(toType)) || !(TypeSemantics.IsPrimitiveType(fromType))) { throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedCast(DescribeClrType(fromClrType), DescribeClrType(toClrType))); } PrimitiveTypeKind fromTypeKind = ((PrimitiveType)fromType.EdmType).PrimitiveTypeKind; PrimitiveTypeKind toTypeKind = ((PrimitiveType)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; } ////// 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. /// 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); } ////// Gets the target type for an Is or As expression. /// /// Input type in model metadata. /// Test or return type. /// Type of operation; used in error reporting. /// Input type in CLR metadata. ///Appropriate target type usage. 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 // 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 CqtExpression TranslateInlineQueryOfT(ObjectQuery inlineQuery) { if (!object.ReferenceEquals(_context, inlineQuery.QueryState.ObjectContext)) { throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedDifferentContexts); } // if the propagated merge option have not been set, use the MergeOption from this inline query if (!_mergeOption.HasValue && inlineQuery.QueryState.UserSpecifiedMergeOption.HasValue) { _mergeOption = inlineQuery.QueryState.UserSpecifiedMergeOption.Value; } LinqExpression queryExpression; if (inlineQuery.QueryState.TryGetExpression(out queryExpression)) { // This inline query is backed by a LINQ expression, which can be inlined into the translation pass // (after suitable normalization). Note that no span merging is required between the Span on the // inline ObjectQuery and the Span produced from the translation of its expression because eLINQ // ObjectQuery instances cannot have a non-null Span (their Span is modeled as a method call in the // LINQ expression tree). queryExpression = NormalizeExpression(queryExpression); return TranslateExpression(queryExpression); } else { // The ObjectQuery should be Entity-SQL-based at this point 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 (!this.IsCompiledQueryMode || objectParameters == null || objectParameters.Count == 0) { // Copy the parameters into the aggregated parameter collection - this will result // in an exception if any duplicate parameter names are encountered. if (objectParameters != null) { foreach (ObjectParameter prm in inlineQuery.QueryState.Parameters) { this.AddParameter(prm.ShallowCopy()); } } resultExpression = esqlState.Parse(_commandTree); } 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. // So that the parameters are never added to the conversion command tree, the referenced ObjectQuery // is parsed using a different query command tree. DbQueryCommandTree replacedTree = new DbQueryCommandTree(_commandTree.MetadataWorkspace, _commandTree.DataSpace); replacedTree.Query = esqlState.Parse(replacedTree); replacedTree.Replace((ExpressionReplacement replacement) => { // Only parameter references are being replaced if (replacement.Current.ExpressionKind != DbExpressionKind.ParameterReference) { return; } DbParameterReferenceExpression paramRef = (DbParameterReferenceExpression)replacement.Current; if (objectParameters.Contains(paramRef.ParameterName)) { // A DbNullExpression is required for null values; DbConstantExpression otherwise. ObjectParameter objParam = objectParameters[paramRef.ParameterName]; if (null == objParam.Value) { replacement.Replacement = replacement.Current.CommandTree.CreateNullExpression(replacement.Current.ResultType); } else { // This will throw if the value is incompatible with the result type. replacement.Replacement = replacement.Current.CommandTree.CreateConstantExpression(objParam.Value, replacement.Current.ResultType); } } } ); resultExpression = _commandTree.Import(replacedTree.Query); } // The Span produced by the converted DbExpression is any Span that was assigned to the referened Entity-SQL ObjectQuery. return AddSpanMapping(resultExpression, inlineQuery.QueryState.Span); } } // creates a CQT cast expression given the source and target CLR type private CqtExpression CreateCastExpression(CqtExpression source, Type toClrType, Type fromClrType) { // see if the source can be normalized as a set CqtExpression 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 _commandTree.CreateCastExpression(source, 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 CqtExpression TranslateLambda(LambdaExpression lambda, CqtExpression input, out DbExpressionBinding binding) { input = NormalizeSetSource(input); // create binding context for this lambda expression binding = _commandTree.CreateExpressionBinding(input); // figure out the binding variable DbVariableReferenceExpression bindingVariable = binding.Variable; _variableNameToInputExpression.Add(bindingVariable.VariableName, input); return TranslateLambda(lambda, bindingVariable); } // 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 CqtExpression TranslateLambda(LambdaExpression lambda, CqtExpression input, out DbGroupExpressionBinding binding) { input = NormalizeSetSource(input); // create binding context for this lambda expression binding = _commandTree.CreateGroupExpressionBinding(input); // figure out the binding variable DbVariableReferenceExpression bindingVariable = binding.Variable; return TranslateLambda(lambda, bindingVariable); } // Utility translator method for lambda expressions. Given a lambda expression and its translated // inputs, translates the lambda expression private CqtExpression TranslateLambda(LambdaExpression lambda, CqtExpression input) { Binding scopeBinding = new Binding(lambda.Parameters[0], input); // push the binding scope _bindingContext.PushBindingScope(scopeBinding); // translate expression within this binding scope CqtExpression result = TranslateExpression(lambda.Body); // 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 CqtExpression NormalizeSetSource(CqtExpression input) { Debug.Assert(null != input); // 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 = _commandTree.CreatePropertyExpression(ExpressionConverter.GroupColumnName, input); } else if (initializerMetadata.Kind == InitializerMetadataKind.EntityCollection) { // for entity collection, redirect the binding to the children input = _commandTree.CreatePropertyExpression(ExpressionConverter.EntityCollectionElementsColumnName, input); } } 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) { LinqExpression argument = callExpression.Arguments[argumentOrdinal]; return (LambdaExpression)GetLambdaExpression(argument); } private LinqExpression GetLambdaExpression(LinqExpression argument) { if (ExpressionType.Lambda == argument.NodeType) { return argument; } else if (ExpressionType.Quote == argument.NodeType) { return GetLambdaExpression(((System.Linq.Expressions.UnaryExpression)argument).Operand); } // see if this can be computed as a closure binding (resolving to an expression) ClosureBinding binding; TypeUsage typeUsage; bool allowLambda = true; if (ClosureBinding.TryCreateClosureBinding(argument, _context.Perspective, allowLambda, _closureCandidates, out binding, out typeUsage) && null != binding.Expression) { AddClosureBinding(binding); return GetLambdaExpression(binding.Expression); } throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UnexpectedLinqLambdaExpressionFormat); } // Translate a LINQ expression acting as a set input to a CQT expression private CqtExpression TranslateSet(LinqExpression linq) { return NormalizeSetSource(TranslateExpression(linq)); } // If the given input source is a GroupBy, tries to produce an optimized translation // The default translation is to 'manually' do group by, the optimized is translating into a // DbGroupByExpression private bool TryRewrite(CqtExpression source, DbExpressionBinding sourceBinding, CqtExpression lambda, out CqtExpression result) { result = null; DbGroupByTemplate optimizedTranslationTemplate; if (_groupByDefaultToOptimizedTranslationMap.TryGetValue(source, out optimizedTranslationTemplate)) { //Create the group by expression DbGroupByExpression optimizedTranslation = _commandTree.CreateGroupByExpression(optimizedTranslationTemplate.Input, optimizedTranslationTemplate.GroupKeys, optimizedTranslationTemplate.Aggregates); DbExpressionBinding optimizedSourceBinding = _commandTree.CreateExpressionBinding(optimizedTranslation); CqtExpression rewrittenLambda; if (GroupByExpressionRewriter.TryRewrite(this, lambda, sourceBinding.VariableName, optimizedSourceBinding.Variable, optimizedTranslationTemplate, out rewrittenLambda)) { result = Project(optimizedSourceBinding, rewrittenLambda); return true; } } return false; } // Translate a LINQ expression to a CQT expression. private CqtExpression TranslateExpression(LinqExpression linq) { Debug.Assert(null != linq); if (_linqExpressionStack.Contains(linq)) { throw EntityUtil.InvalidOperation(Strings.ELinq_CycleDetected); } _linqExpressionStack.Push(linq); try { ClosureBinding binding; TypeUsage bindingType; bool allowLambda = false; CqtExpression result; if (_bindingContext.TryGetBoundExpression(linq, out result)) { // see if the LINQ expression has been bound to some CQT expression already // (e.g. parameter expressions and compiled query parameters) return result; } else if (ClosureBinding.TryCreateClosureBinding(linq, _context.Perspective, allowLambda, _closureCandidates, out binding, out bindingType)) { // see if the expression is a closure binding if (!IsCompiledQueryMode) { AddClosureBinding(binding); } if (null != binding.Parameter) { if (IsCompiledQueryMode) { // pretend it was a constant expression that we found ConstantExpression constant = Expression.Constant(binding.Parameter.Value, binding.Parameter.ParameterType); return TranslateExpression(constant); } else { // Note that parameters are independently added to the command tree, not just // the query context. This is because a CQT expression tree is invalid if // its parameters are not known, and a valid tree is required (in addition to // the query context) to construct an ObjectQueryinstance. _commandTree.AddParameter(binding.Parameter.Name, bindingType); // return parameter taking the closure value return _commandTree.CreateParameterReferenceExpression(binding.Parameter.Name); } } else { Debug.Assert(null != binding.Query, "binding must be either to a query, expression (handled by TranslateLambda), or a parameter"); return TranslateInlineQueryOfT(binding.Query); } } ObjectQuery queryOfT; if (ExpressionEvaluator.TryEvaluateRootQuery(_bindingContext, linq, out queryOfT)) { return TranslateInlineQueryOfT(queryOfT); } // translate to a CQT expression Translator translator; if (s_translators.TryGetValue(linq.NodeType, out translator)) { return translator.Translate(this, linq); } else { throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UnknownLinqNodeType, -1, linq.NodeType.ToString()); } } finally { _linqExpressionStack.Pop(); } } // Cast expression to align types between CQT and eLINQ private CqtExpression AlignTypes(CqtExpression cqt, Type toClrType) { Type fromClrType = null; // not used in this code path TypeUsage toType = GetCastTargetType(cqt.ResultType, toClrType, fromClrType, false); if (null != toType) { return _commandTree.CreateCastExpression(cqt, 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 (_context.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) { // Ensure the metadata for this object type is loaded _commandTree.MetadataWorkspace.LoadAssemblyForType(linqType, System.Reflection.Assembly.GetCallingAssembly()); // Remove nullable Type nonNullableType = TypeSystem.GetNonNullableType(linqType); // 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; } } // Retrieve type from map return _context.Perspective.TryGetTypeByName( nonNullableType.FullName, false, // ignoreCase out type); } /// /// Utility method validating type for comparison ops (isNull, equals, etc.). /// Only primitive types, entity types, and simple row types (no IGrouping/EntityCollection) are /// supported. /// private static void VerifyTypeSupportedForComparison(Type clrType, TypeUsage edmType, StackmemberPath) { // 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.EntityType: 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), Strings.ELinq_PrimitiveTypesSample)); } 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(), Strings.ELinq_PrimitiveTypesSample)); } } private static void VerifyRowTypeSupportedForComparison(Type clrType, RowType rowType, Stack memberPath) { foreach (EdmMember member in rowType.Properties) { if (null == memberPath) { memberPath = new Stack (); } memberPath.Push(member); VerifyTypeSupportedForComparison(clrType, member.TypeUsage, memberPath); memberPath.Pop(); } } /// /// Describe type for exception message. /// 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); } ////// Creates an implementation of IsNull. Throws exception when operand type is not supported. /// private CqtExpression CreateIsNullExpression(CqtExpression operand, Type operandClrType) { VerifyTypeSupportedForComparison(operandClrType, operand.ResultType, null); return _commandTree.CreateIsNullExpression(operand); } ////// Creates an implementation of equals using the given pattern. Throws exception when argument types /// are not supported for equals comparison. /// private CqtExpression CreateEqualsExpression(CqtExpression left, CqtExpression right, EqualsPattern pattern, Type leftClrType, Type rightClrType) { VerifyTypeSupportedForComparison(leftClrType, left.ResultType, null); VerifyTypeSupportedForComparison(rightClrType, right.ResultType, null); return RecursivelyRewriteEqualsExpression(left, right, pattern); } private CqtExpression RecursivelyRewriteEqualsExpression(CqtExpression left, CqtExpression 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)) { CqtExpression shreddedEquals = null; // if the types are the same, use struct equivalence semantics foreach (EdmProperty property in leftType.Properties) { DbPropertyExpression leftElement = _commandTree.CreatePropertyExpression( property, left); DbPropertyExpression rightElement = _commandTree.CreatePropertyExpression( property, right); CqtExpression elementsEquals = RecursivelyRewriteEqualsExpression( leftElement, rightElement, pattern); // build up and expression if (null == shreddedEquals) { shreddedEquals = elementsEquals; } else { shreddedEquals = _commandTree.CreateAndExpression(shreddedEquals, 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 _commandTree.CreateFalseExpression(); } } 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 CqtExpression ImplementEquality(CqtExpression left, CqtExpression right, EqualsPattern pattern) { switch (left.ExpressionKind) { case DbExpressionKind.Constant: switch (right.ExpressionKind) { case DbExpressionKind.Constant: // constant EQ constant return _commandTree.CreateEqualsExpression(left, right); case DbExpressionKind.Null: // null EQ constant --> false return _commandTree.CreateFalseExpression(); 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 _commandTree.CreateFalseExpression(); case DbExpressionKind.Null: // null EQ null --> true return _commandTree.CreateTrueExpression(); default: // null EQ right --> right IS NULL return _commandTree.CreateIsNullExpression(right); } 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 _commandTree.CreateIsNullExpression(left); default: return ImplementEqualityUnknownArguments(left, right, pattern); } } } // Generate an equality expression with one unknown operator and private CqtExpression ImplementEqualityConstantAndUnknown( System.Data.Common.CommandTrees.DbConstantExpression constant, CqtExpression unknown, EqualsPattern pattern) { switch (pattern) { case EqualsPattern.Store: case EqualsPattern.PositiveNullEquality: // either both are non-null, or one is null and the predicate result is undefined return _commandTree.CreateEqualsExpression(constant, unknown); default: Debug.Fail("unknown pattern"); return null; } } // Generate an equality expression where the values of the left and right operands are completely unknown private CqtExpression ImplementEqualityUnknownArguments(CqtExpression left, CqtExpression right, EqualsPattern pattern) { switch (pattern) { case EqualsPattern.Store: // left EQ right return _commandTree.CreateEqualsExpression(left, right); case EqualsPattern.PositiveNullEquality: // left EQ right OR (left IS NULL AND right IS NULL) { CqtExpression leftIsNull = _commandTree.CreateIsNullExpression(left); CqtExpression rightIsNull = _commandTree.CreateIsNullExpression(right); CqtExpression equals = _commandTree.CreateEqualsExpression(left, right); return _commandTree.CreateOrExpression(equals, _commandTree.CreateAndExpression(leftIsNull, rightIsNull)); } default: Debug.Fail("unexpected pattern"); return null; } } #endregion #region Helper Methods Shared by Translators ////// Translates the arguments into CqtExpressions /// and creates a canonical function with the given functionName and these arguments /// /// Should represent a non-aggregate canonical function /// Passed only for error handling purposes /// ///private DbFunctionExpression TranslateIntoCanonicalFunction(string functionName, LinqExpression linqExpression, params LinqExpression[] linqArguments) { DbExpression[] translatedArguments = new DbExpression[linqArguments.Length]; for (int i = 0; i < linqArguments.Length; i++) { translatedArguments[i] = TranslateExpression(linqArguments[i]); } return CreateCanonicalFunction(functionName, linqExpression, translatedArguments); } /// /// Creates a canonical function with the given name and the given arguments /// /// Should represent a non-aggregate canonical function /// Passed only for error handling purposes /// ///private DbFunctionExpression CreateCanonicalFunction(string functionName, LinqExpression linqExpression, params CqtExpression[] translatedArguments) { List translatedArgumentTypes = new List (translatedArguments.Length); foreach (CqtExpression translatedArgument in translatedArguments) { translatedArgumentTypes.Add(translatedArgument.ResultType); } EdmFunction function = FindCanonicalFunction(functionName, translatedArgumentTypes, false /* isGroupAggregateFunction */, linqExpression); return _commandTree.CreateFunctionExpression(function, translatedArguments); } /// /// Finds a canonical function wiht the given functionName and argumentTypes /// /// /// /// /// ///private EdmFunction FindCanonicalFunction(string functionName, IList argumentTypes, bool isGroupAggregateFunction, LinqExpression linqExpression) { // find the function IList functions; if (!_typeResolver.TryGetFunctionFromMetadata(functionName, EdmNamespaceName, true /*ignoreCase*/, out functions)) { ThrowUnresolvableCanonicalFunction(linqExpression); } Debug.Assert(null != functions && functions.Count > 0, "provider functions must not be null or empty"); bool isAmbiguous; EdmFunction function = TypeResolver.ResolveFunctionOverloads(functions, argumentTypes, isGroupAggregateFunction, out isAmbiguous); if (isAmbiguous || null == function) { ThrowUnresolvableStoreFunction(linqExpression); } return function; } /// /// Helper method for FindCanonicalFunction /// /// private static void ThrowUnresolvableCanonicalFunction(LinqExpression linqExpression) { if (linqExpression.NodeType == ExpressionType.Call) { MethodInfo methodInfo = ((MethodCallExpression)linqExpression).Method; throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableCanonicalFunctionForMethod(methodInfo, methodInfo.DeclaringType)); } else if (linqExpression.NodeType == ExpressionType.MemberAccess) { string memberName; Type memberType; MemberInfo memberInfo = TypeSystem.PropertyOrField(((MemberExpression)linqExpression).Member, out memberName, out memberType); throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableCanonicalFunctionForMember(memberInfo, memberInfo.DeclaringType)); } throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableCanonicalFunctionForExpression(linqExpression.NodeType)); } ////// Helper method for FindCanonicalFunction /// /// private static void ThrowUnresolvableStoreFunction(LinqExpression linqExpression) { if (linqExpression.NodeType == ExpressionType.Call) { MethodInfo methodInfo = ((MethodCallExpression)linqExpression).Method; throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnresolvableStoreFunctionForMethod(methodInfo, methodInfo.DeclaringType)); } else if (linqExpression.NodeType == ExpressionType.MemberAccess) { string memberName; Type memberType; MemberInfo memberInfo = TypeSystem.PropertyOrField(((MemberExpression)linqExpression).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(linqExpression.NodeType)); } private DbNewInstanceExpression CreateNewRowExpression(List> columns, InitializerMetadata initializerMetadata) { List propertyValues = new List (columns.Count); List properties = new List (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 _commandTree.CreateNewInstanceExpression(typeUsage, propertyValues); } #endregion #region Helper Structures // Mirrors the structure of DbGroupByExpression // It is used for incrementally constructing a DbGroupByExpression // (the actual DbGroupByExpression cannot be used for this purpose because it is immutable) private class DbGroupByTemplate { private DbGroupExpressionBinding _input; private List > _groupKeys; private List > _aggregates; public DbGroupByTemplate(DbGroupExpressionBinding input) { _input = input; _groupKeys = new List >(); _aggregates = new List >(); } public List > GroupKeys { get { return _groupKeys; } } public List > Aggregates { get { return _aggregates; } } public DbGroupExpressionBinding Input { get { return _input; } } } #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: // // Store: left EQ right // NullEquality: left EQ right OR (left IS NULL AND right IS NULL) // PositiveEquality: left EQ right // // 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 PositiveNullEquality, // null == null is 'true', null == (not null) us undefined } #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- RuntimeUtils.cs
- GridViewDeletedEventArgs.cs
- XmlILTrace.cs
- HtmlWindowCollection.cs
- OracleException.cs
- CodeExpressionStatement.cs
- GroupStyle.cs
- WebServiceClientProxyGenerator.cs
- FaultImportOptions.cs
- IconConverter.cs
- ButtonRenderer.cs
- BamlResourceContent.cs
- LayoutManager.cs
- PagerSettings.cs
- DiscoveryClientBindingElement.cs
- TypeToStringValueConverter.cs
- DispatcherExceptionFilterEventArgs.cs
- ListManagerBindingsCollection.cs
- UInt16Storage.cs
- InfoCardXmlSerializer.cs
- PageAsyncTaskManager.cs
- ComponentResourceKey.cs
- FieldValue.cs
- DictionaryCustomTypeDescriptor.cs
- DataGridViewControlCollection.cs
- BooleanExpr.cs
- XmlILTrace.cs
- ProcessInfo.cs
- NamedPipeTransportElement.cs
- JoinCqlBlock.cs
- PermissionToken.cs
- OpenFileDialog.cs
- HighlightVisual.cs
- DbConnectionInternal.cs
- DiagnosticSection.cs
- SmtpDateTime.cs
- XmlDeclaration.cs
- SchemaNotation.cs
- COM2ExtendedTypeConverter.cs
- BamlTreeUpdater.cs
- LocalizationComments.cs
- RawStylusActions.cs
- Error.cs
- FilterEventArgs.cs
- ChineseLunisolarCalendar.cs
- DataRelation.cs
- AccessViolationException.cs
- ReliableOutputSessionChannel.cs
- TextEditorThreadLocalStore.cs
- UDPClient.cs
- InputManager.cs
- GlobalizationAssembly.cs
- TextHidden.cs
- InternalDuplexChannelListener.cs
- WindowsScroll.cs
- TypeConverterHelper.cs
- BamlStream.cs
- CoreSwitches.cs
- BitmapEffectRenderDataResource.cs
- EditCommandColumn.cs
- BooleanProjectedSlot.cs
- Vector3DConverter.cs
- xml.cs
- WebPartChrome.cs
- SplitterEvent.cs
- CalendarData.cs
- HttpCookie.cs
- WmfPlaceableFileHeader.cs
- NameSpaceEvent.cs
- SchemaTypeEmitter.cs
- WrappedIUnknown.cs
- ImageDrawing.cs
- _DigestClient.cs
- DefaultTextStoreTextComposition.cs
- DateTimeValueSerializer.cs
- CryptoProvider.cs
- OptionalColumn.cs
- MappedMetaModel.cs
- SafeViewOfFileHandle.cs
- BaseAutoFormat.cs
- SelectManyQueryOperator.cs
- InstanceData.cs
- ProviderConnectionPointCollection.cs
- EventLogger.cs
- CreateUserWizardStep.cs
- ElementMarkupObject.cs
- QilSortKey.cs
- StreamAsIStream.cs
- Collection.cs
- ObjectTag.cs
- EncryptedXml.cs
- StreamReader.cs
- _ProxyRegBlob.cs
- ZoneMembershipCondition.cs
- EntityConnectionStringBuilder.cs
- dataprotectionpermissionattribute.cs
- MainMenu.cs
- Logging.cs
- _HeaderInfo.cs
- FunctionImportMapping.cs