internal override SpanTrackingInfo CreateEntitySpanTrackingInfo(DbExpression expression, EntityType entityType) { var tracking = new SpanTrackingInfo(); var currentInfo = _currentSpanPath.Peek(); if (currentInfo.Children != null) { // The current SpanPathInfo instance on the top of the span path stack indicates // which navigation properties should be retrieved from this Entity-typed expression // and also specifies (in the form of child SpanPathInfo instances) which sub-paths // must be expanded for each of those navigation properties. // The SpanPathInfo instance may be the root instance or a SpanPathInfo that represents a sub-path. var idx = 1; // SpanRoot is always the first (zeroth) column, full- and relationship-span columns follow. foreach (var nextInfo in currentInfo.Children) { // If the tracking information was not initialized yet, do so now. if (null == tracking.ColumnDefinitions) { tracking = InitializeTrackingInfo(RelationshipSpan); } // Create a property expression that retrieves the specified navigation property from the Entity-typed expression. // Note that the expression is cloned since it may be used as the instance of multiple property expressions. DbExpression columnDef = expression.Property(nextInfo.Key); // Rewrite the result of the navigation property. This is required for two reasons: // 1. To continue spanning the current Include path. // 2. To apply relationship span to the Entity or EntityCollection produced by the navigation property, if necessary. // Consider an Include path of "Order" for a query that returns OrderLines - the Include'd Orders should have // their associated Customer relationship spanned. // Note that this will recursively call this method with the Entity type of the result of the // navigation property, which will in turn call loop through the sub-paths of this navigation // property and adjust the stack to track which Include path is being expanded and which // element of that path is considered 'current'. _currentSpanPath.Push(nextInfo.Value); columnDef = Rewrite(columnDef); _currentSpanPath.Pop(); // Add a new column to the tracked columns using the rewritten column definition tracking.ColumnDefinitions.Add(new KeyValuePair <string, DbExpression>(tracking.ColumnNames.Next(), columnDef)); var targetEnd = GetNavigationPropertyTargetEnd(nextInfo.Key); tracking.SpannedColumns[idx] = targetEnd; // If full span and relationship span are both required, a relationship span may be rendered // redundant by an already added full span. Therefore the association ends that have been expanded // as part of full span are tracked using a dictionary. if (RelationshipSpan) { tracking.FullSpannedEnds[targetEnd] = true; } idx++; } } return(tracking); }
internal SpanTrackingInfo InitializeTrackingInfo(bool createAssociationEndTrackingInfo) { SpanTrackingInfo info = new SpanTrackingInfo(); info.ColumnDefinitions = new List <KeyValuePair <string, DbExpression> >(); info.ColumnNames = new AliasGenerator(string.Format(CultureInfo.InvariantCulture, "Span{0}_Column", _spanCount)); info.SpannedColumns = new Dictionary <int, AssociationEndMember>(); if (createAssociationEndTrackingInfo) { info.FullSpannedEnds = new Dictionary <AssociationEndMember, bool>(); } return(info); }
private DbExpression RewriteEntity(DbExpression expression, EntityType entityType) { // If the expression is an Entity constructor, spanning will not produce any useful results // (null for an Entity/Ref navigation property, or an empty collection for a Collection // of Entity/Ref navigation property) since a Ref produced from the constructed Entity // will not indicate an Entity set, and therefore no Ref created against any Entity set // in the container can possibly be a match for it. if (DbExpressionKind.NewInstance == expression.ExpressionKind) { return(expression); } // Save the span count for later use. _spanCount++; int thisSpan = _spanCount; SpanTrackingInfo tracking = CreateEntitySpanTrackingInfo(expression, entityType); // If relationship span is required then attempt to span any appropriate relationship ends. List <KeyValuePair <AssociationEndMember, AssociationEndMember> > relationshipSpans = null; relationshipSpans = GetRelationshipSpanEnds(entityType); // Is the Entity type of this expression valid as the source of at least one relationship span? if (relationshipSpans != null) { // If the span tracking information was not initialized by CreateEntitySpanTrackingInfo, // then do so now as relationship span rewrites need to be tracked. if (null == tracking.ColumnDefinitions) { tracking = InitializeTrackingInfo(false); } // Track column index to span information, starting at the current column count (which could be zero) plus 1. // 1 is added because the column containing the root entity will be added later to provide column zero. int idx = tracking.ColumnDefinitions.Count + 1; // For all applicable relationship spans that were identified... foreach (KeyValuePair <AssociationEndMember, AssociationEndMember> relSpan in relationshipSpans) { // If the specified association end member was already full-spanned then the full entity // will be returned in the query and there is no need to relationship-span this end to produce // another result column that contains the Entity key of the full entity. // Hence the relationship span is only added if there are no full-span columns or the full-span // columns do not indicate that they include the target association end member of this relationship span. if (null == tracking.FullSpannedEnds || !tracking.FullSpannedEnds.ContainsKey(relSpan.Value)) { // If the source Ref is already available, because the currently spanned Entity is // the result of a Relationship Navigation operation from that Ref, then use the source // Ref directly rather than introducing a new Navigation operation. DbExpression columnDef = null; if (!TryGetNavigationSource(relSpan.Value, out columnDef)) { // Add a new column defined by the navigation required to reach the targeted association end // and update the column -> association end map to include an entry for this new column. DbExpression navSource = expression.GetEntityRef(); columnDef = navSource.NavigateAllowingAllRelationshipsInSameTypeHierarchy(relSpan.Key, relSpan.Value); } tracking.ColumnDefinitions.Add( new KeyValuePair <string, DbExpression>( tracking.ColumnNames.Next(), columnDef ) ); tracking.SpannedColumns[idx] = relSpan.Value; // Increment the tracked column count idx++; } } } // If no spanned columns have been added then simply return the original expression if (null == tracking.ColumnDefinitions) { _spanCount--; return(expression); } // Add the original entity-producing expression as the first (root) span column. tracking.ColumnDefinitions.Insert( 0, new KeyValuePair <string, DbExpression>( string.Format(CultureInfo.InvariantCulture, "Span{0}_SpanRoot", thisSpan), expression ) ); // Create the span row-producing NewInstanceExpression from which the span RowType can be retrieved. DbExpression spannedExpression = DbExpressionBuilder.NewRow(tracking.ColumnDefinitions); // Update the rowtype -> spaninfo map for the newly created row type instance. RowType spanRowType = (RowType)spannedExpression.ResultType.EdmType; AddSpanMap(spanRowType, tracking.SpannedColumns); // Return the rewritten expression return(spannedExpression); }
internal SpanTrackingInfo InitializeTrackingInfo(bool createAssociationEndTrackingInfo) { SpanTrackingInfo info = new SpanTrackingInfo(); info.ColumnDefinitions = new List<KeyValuePair<string, DbExpression>>(); info.ColumnNames = new AliasGenerator(string.Format(CultureInfo.InvariantCulture, "Span{0}_Column", _spanCount)); info.SpannedColumns = new Dictionary<int, AssociationEndMember>(); if (createAssociationEndTrackingInfo) { info.FullSpannedEnds = new Dictionary<AssociationEndMember, bool>(); } return info; }
internal override SpanTrackingInfo CreateEntitySpanTrackingInfo(DbExpression expression, EntityType entityType) { var tracking = new SpanTrackingInfo(); var currentInfo = _currentSpanPath.Peek(); if (currentInfo.Children != null) { // The current SpanPathInfo instance on the top of the span path stack indicates // which navigation properties should be retrieved from this Entity-typed expression // and also specifies (in the form of child SpanPathInfo instances) which sub-paths // must be expanded for each of those navigation properties. // The SpanPathInfo instance may be the root instance or a SpanPathInfo that represents a sub-path. var idx = 1; // SpanRoot is always the first (zeroth) column, full- and relationship-span columns follow. foreach (var nextInfo in currentInfo.Children) { // If the tracking information was not initialized yet, do so now. if (null == tracking.ColumnDefinitions) { tracking = InitializeTrackingInfo(RelationshipSpan); } // Create a property expression that retrieves the specified navigation property from the Entity-typed expression. // Note that the expression is cloned since it may be used as the instance of multiple property expressions. DbExpression columnDef = expression.Property(nextInfo.Key); // Rewrite the result of the navigation property. This is required for two reasons: // 1. To continue spanning the current Include path. // 2. To apply relationship span to the Entity or EntityCollection produced by the navigation property, if necessary. // Consider an Include path of "Order" for a query that returns OrderLines - the Include'd Orders should have // their associated Customer relationship spanned. // Note that this will recursively call this method with the Entity type of the result of the // navigation property, which will in turn call loop through the sub-paths of this navigation // property and adjust the stack to track which Include path is being expanded and which // element of that path is considered 'current'. _currentSpanPath.Push(nextInfo.Value); columnDef = Rewrite(columnDef); _currentSpanPath.Pop(); // Add a new column to the tracked columns using the rewritten column definition tracking.ColumnDefinitions.Add(new KeyValuePair<string, DbExpression>(tracking.ColumnNames.Next(), columnDef)); var targetEnd = GetNavigationPropertyTargetEnd(nextInfo.Key); tracking.SpannedColumns[idx] = targetEnd; // If full span and relationship span are both required, a relationship span may be rendered // redundant by an already added full span. Therefore the association ends that have been expanded // as part of full span are tracked using a dictionary. if (RelationshipSpan) { tracking.FullSpannedEnds[targetEnd] = true; } idx++; } } return tracking; }