You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -0,0 +1,263 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="ObjectFullSpanRewriter.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupowner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace System.Data.Objects.Internal
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Diagnostics;
|
||||
|
||||
internal class ObjectFullSpanRewriter : ObjectSpanRewriter
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a node in the 'Include' navigation property tree
|
||||
/// built from the list of SpanPaths on the Span object with which
|
||||
/// the FullSpanRewriter is constructed.
|
||||
/// </summary>
|
||||
private class SpanPathInfo
|
||||
{
|
||||
internal SpanPathInfo(EntityType declaringType)
|
||||
{
|
||||
this.DeclaringType = declaringType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The effective Entity type of this node in the tree
|
||||
/// </summary>
|
||||
internal EntityType DeclaringType;
|
||||
|
||||
/// <summary>
|
||||
/// Describes the navigation properties that should be retrieved
|
||||
/// from this node in the tree and the Include sub-paths that extend
|
||||
/// from each of those navigation properties
|
||||
/// </summary>
|
||||
internal Dictionary<NavigationProperty, SpanPathInfo> Children;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maintains a reference to the SpanPathInfo tree node representing the
|
||||
/// current position in the 'Include' path that is currently being expanded.
|
||||
/// </summary>
|
||||
private Stack<SpanPathInfo> _currentSpanPath = new Stack<SpanPathInfo>();
|
||||
|
||||
internal ObjectFullSpanRewriter(DbCommandTree tree, DbExpression toRewrite, Span span, AliasGenerator aliasGenerator)
|
||||
: base(tree, toRewrite, aliasGenerator)
|
||||
{
|
||||
Debug.Assert(span != null, "Span cannot be null");
|
||||
Debug.Assert(span.SpanList.Count > 0, "At least one span path is required");
|
||||
|
||||
// Retrieve the effective 'T' of the ObjectQuery<T> that produced
|
||||
// the Command Tree that is being rewritten. This could be either
|
||||
// literally 'T' or Collection<T>.
|
||||
EntityType entityType = null;
|
||||
if (!TryGetEntityType(this.Query.ResultType, out entityType))
|
||||
{
|
||||
// If the result type of the query is neither an Entity type nor a collection
|
||||
// type with an Entity element type, then full Span is currently not allowed.
|
||||
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ObjectQuery_Span_IncludeRequiresEntityOrEntityCollection);
|
||||
}
|
||||
|
||||
// Construct the SpanPathInfo navigation property tree using the
|
||||
// list of Include Span paths from the Span object:
|
||||
// Create a SpanPathInfo instance that represents the root of the tree
|
||||
// and takes its Entity type from the Entity type of the result type of the query.
|
||||
SpanPathInfo spanRoot = new SpanPathInfo(entityType);
|
||||
|
||||
// Populate the tree of navigation properties based on the navigation property names
|
||||
// in the Span paths from the Span object. Commonly rooted span paths are merged, so
|
||||
// that paths of "Customer.Order" and "Customer.Address", for example, will share a
|
||||
// common SpanPathInfo for "Customer" in the Children collection of the root SpanPathInfo,
|
||||
// and that SpanPathInfo will contain one child for "Order" and another for "Address".
|
||||
foreach (Span.SpanPath path in span.SpanList)
|
||||
{
|
||||
AddSpanPath(spanRoot, path.Navigations);
|
||||
}
|
||||
|
||||
// The 'current' span path is initialized to the root of the Include span tree
|
||||
_currentSpanPath.Push(spanRoot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the Include span tree with appropriate branches for the Include path
|
||||
/// represented by the specified list of navigation property names.
|
||||
/// </summary>
|
||||
/// <param name="parentInfo">The root SpanPathInfo</param>
|
||||
/// <param name="navPropNames">A list of navigation property names that describes a single Include span path</param>
|
||||
private void AddSpanPath(SpanPathInfo parentInfo, List<string> navPropNames)
|
||||
{
|
||||
ConvertSpanPath(parentInfo, navPropNames, 0);
|
||||
}
|
||||
|
||||
private void ConvertSpanPath(SpanPathInfo parentInfo, List<string> navPropNames, int pos)
|
||||
{
|
||||
// Attempt to retrieve the next navigation property from the current entity type
|
||||
// using the name of the current navigation property in the Include path.
|
||||
NavigationProperty nextNavProp = null;
|
||||
if (!parentInfo.DeclaringType.NavigationProperties.TryGetValue(navPropNames[pos], true, out nextNavProp))
|
||||
{
|
||||
// The navigation property name is not valid for this Entity type
|
||||
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ObjectQuery_Span_NoNavProp(parentInfo.DeclaringType.FullName, navPropNames[pos]));
|
||||
}
|
||||
|
||||
// The navigation property was retrieved, an entry for it must be ensured in the Children
|
||||
// collection of the parent SpanPathInfo instance.
|
||||
// If the parent's Children collection does not exist then instantiate it now:
|
||||
if (null == parentInfo.Children)
|
||||
{
|
||||
parentInfo.Children = new Dictionary<NavigationProperty, SpanPathInfo>();
|
||||
}
|
||||
|
||||
// If a sub-path that begins with the current navigation property name was already
|
||||
// encountered, then a SpanPathInfo for this navigation property may already exist
|
||||
// in the Children dictionary...
|
||||
SpanPathInfo nextChild = null;
|
||||
if (!parentInfo.Children.TryGetValue(nextNavProp, out nextChild))
|
||||
{
|
||||
// ... otherwise, create a new SpanPathInfo instance that this navigation
|
||||
// property maps to and ensure its presence in the Children dictionary.
|
||||
nextChild = new SpanPathInfo(EntityTypeFromResultType(nextNavProp));
|
||||
parentInfo.Children[nextNavProp] = nextChild;
|
||||
}
|
||||
|
||||
// If this navigation property is not the end of the span path then
|
||||
// increment the position and recursively call ConvertSpanPath, specifying
|
||||
// the (retrieved or newly-created) SpanPathInfo of this navigation property
|
||||
// as the new 'parent' info.
|
||||
if (pos < navPropNames.Count - 1)
|
||||
{
|
||||
ConvertSpanPath(nextChild, navPropNames, pos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the Entity (result or element) type produced by a Navigation Property.
|
||||
/// </summary>
|
||||
/// <param name="navProp">The navigation property</param>
|
||||
/// <returns>
|
||||
/// The Entity type produced by the navigation property.
|
||||
/// This may be the immediate result type (if the result is at most one)
|
||||
/// or the element type of the result type, otherwise.
|
||||
/// </returns>
|
||||
private static EntityType EntityTypeFromResultType(NavigationProperty navProp)
|
||||
{
|
||||
EntityType retType = null;
|
||||
TryGetEntityType(navProp.TypeUsage, out retType);
|
||||
// Currently, navigation properties may only return an Entity or Collection<Entity> result
|
||||
Debug.Assert(retType != null, "Navigation property has non-Entity and non-Entity collection result type?");
|
||||
return retType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the Entity (result or element) type referenced by the specified TypeUsage, if
|
||||
/// its EdmType is an Entity type or a collection type with an Entity element type.
|
||||
/// </summary>
|
||||
/// <param name="resultType">The TypeUsage that provides the EdmType to examine</param>
|
||||
/// <param name="entityType">The referenced Entity (element) type, if present.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <paramref name="resultType"/> is an Entity type or a
|
||||
/// collection type with an Entity element type; otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
private static bool TryGetEntityType(TypeUsage resultType, out EntityType entityType)
|
||||
{
|
||||
// If the result type is an Entity, then simply use that type.
|
||||
if (BuiltInTypeKind.EntityType == resultType.EdmType.BuiltInTypeKind)
|
||||
{
|
||||
entityType = (EntityType)resultType.EdmType;
|
||||
return true;
|
||||
}
|
||||
else if (BuiltInTypeKind.CollectionType == resultType.EdmType.BuiltInTypeKind)
|
||||
{
|
||||
// If the result type of the query is a collection, attempt to extract
|
||||
// the element type of the collection and determine if it is an Entity type.
|
||||
EdmType elementType = ((CollectionType)resultType.EdmType).TypeUsage.EdmType;
|
||||
if (BuiltInTypeKind.EntityType == elementType.BuiltInTypeKind)
|
||||
{
|
||||
entityType = (EntityType)elementType;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
entityType = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility method to retrieve the 'To' AssociationEndMember of a NavigationProperty
|
||||
/// </summary>
|
||||
/// <param name="property">The navigation property</param>
|
||||
/// <returns>The AssociationEndMember that is the target of the navigation operation represented by the NavigationProperty</returns>
|
||||
private AssociationEndMember GetNavigationPropertyTargetEnd(NavigationProperty property)
|
||||
{
|
||||
AssociationType relationship = this.Metadata.GetItem<AssociationType>(property.RelationshipType.FullName, DataSpace.CSpace);
|
||||
Debug.Assert(relationship.AssociationEndMembers.Contains(property.ToEndMember.Name), "Association does not declare member referenced by Navigation property?");
|
||||
return relationship.AssociationEndMembers[property.ToEndMember.Name];
|
||||
}
|
||||
|
||||
internal override SpanTrackingInfo CreateEntitySpanTrackingInfo(DbExpression expression, EntityType entityType)
|
||||
{
|
||||
SpanTrackingInfo tracking = new SpanTrackingInfo();
|
||||
|
||||
SpanPathInfo 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.
|
||||
int idx = 1; // SpanRoot is always the first (zeroth) column, full- and relationship-span columns follow.
|
||||
foreach (KeyValuePair<NavigationProperty, SpanPathInfo> nextInfo in currentInfo.Children)
|
||||
{
|
||||
// If the tracking information was not initialized yet, do so now.
|
||||
if (null == tracking.ColumnDefinitions)
|
||||
{
|
||||
tracking = InitializeTrackingInfo(this.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 = this.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));
|
||||
AssociationEndMember 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 (this.RelationshipSpan)
|
||||
{
|
||||
tracking.FullSpannedEnds[targetEnd] = true;
|
||||
}
|
||||
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
return tracking;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user