You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			884 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			884 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| //---------------------------------------------------------------------
 | |
| // <copyright file="ObjectSpanRewriter.cs" company="Microsoft">
 | |
| //      Copyright (c) Microsoft Corporation.  All rights reserved.
 | |
| // </copyright>
 | |
| //
 | |
| // @owner Microsoft
 | |
| // @backupowner Microsoft
 | |
| //---------------------------------------------------------------------
 | |
| 
 | |
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Diagnostics;
 | |
| using System.Data.Common;
 | |
| using System.Data.Common.Utils;
 | |
| using System.Data.Metadata.Edm;
 | |
| using System.Data.Common.CommandTrees;
 | |
| using System.Globalization;
 | |
| using System.Data.Common.CommandTrees.ExpressionBuilder;
 | |
| 
 | |
| namespace System.Data.Objects.Internal
 | |
| {
 | |
|     /// <summary>
 | |
|     /// Responsible for performing Relationship-span only rewrites over a Command Tree rooted
 | |
|     /// by the <see cref="Query"/> property. Virtual methods provide an opportunity for derived
 | |
|     /// classes to implement Full-span rewrites.
 | |
|     /// </summary>
 | |
|     internal class ObjectSpanRewriter
 | |
|     {
 | |
|         internal static bool EntityTypeEquals(EntityTypeBase entityType1, EntityTypeBase entityType2)
 | |
|         {
 | |
|             return object.ReferenceEquals(entityType1, entityType2);
 | |
|         }
 | |
| 
 | |
|         #region Private members
 | |
| 
 | |
|         private int _spanCount;
 | |
|         private SpanIndex _spanIndex;
 | |
|         private DbExpression _toRewrite;
 | |
|         private bool _relationshipSpan;
 | |
|         private DbCommandTree _tree;
 | |
|         private Stack<NavigationInfo> _navSources = new Stack<NavigationInfo>();
 | |
|         private readonly AliasGenerator _aliasGenerator;
 | |
|         
 | |
|         #endregion
 | |
| 
 | |
|         #region 'Public' API
 | |
| 
 | |
|         internal static bool TryRewrite(DbQueryCommandTree tree, Span span, MergeOption mergeOption, AliasGenerator aliasGenerator, out DbExpression newQuery, out SpanIndex spanInfo)
 | |
|         {
 | |
|             newQuery = null;
 | |
|             spanInfo = null;
 | |
| 
 | |
|             ObjectSpanRewriter rewriter = null;
 | |
|             bool requiresRelationshipSpan = Span.RequiresRelationshipSpan(mergeOption);
 | |
| 
 | |
|             // Potentially perform a rewrite for span.
 | |
|             // Note that the public 'Span' property is NOT used to retrieve the Span instance
 | |
|             // since this forces creation of a Span object that may not be required.
 | |
|             if (span != null && span.SpanList.Count > 0)
 | |
|             {
 | |
|                 rewriter = new ObjectFullSpanRewriter(tree, tree.Query, span, aliasGenerator);
 | |
|             }
 | |
|             else if (requiresRelationshipSpan)
 | |
|             {
 | |
|                 rewriter = new ObjectSpanRewriter(tree, tree.Query, aliasGenerator);
 | |
|             }
 | |
| 
 | |
|             if (rewriter != null)
 | |
|             {
 | |
|                 rewriter.RelationshipSpan = requiresRelationshipSpan;
 | |
|                 newQuery = rewriter.RewriteQuery();
 | |
|                 if (newQuery != null)
 | |
|                 {
 | |
|                     Debug.Assert(rewriter.SpanIndex != null || tree.Query.ResultType.EdmEquals(newQuery.ResultType), "Query was rewritten for Span but no SpanIndex was created?");
 | |
|                     spanInfo = rewriter.SpanIndex;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return (spanInfo != null);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Constructs a new ObjectSpanRewriter that will attempt to apply spanning to the specified query
 | |
|         /// (represented as a DbExpression) when <see cref="RewriteQuery"/> is called.
 | |
|         /// </summary>
 | |
|         /// <param name="toRewrite">A <see cref="DbExpression"/> representing the query to span.</param>
 | |
|         internal ObjectSpanRewriter(DbCommandTree tree, DbExpression toRewrite, AliasGenerator aliasGenerator)
 | |
|         {
 | |
|             Debug.Assert(toRewrite != null, "Expression to rewrite cannot be null");
 | |
| 
 | |
|             _toRewrite = toRewrite;
 | |
|             _tree = tree;
 | |
|             _aliasGenerator = aliasGenerator;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets the metadata workspace the will be used to retrieve required metadata, for example association types.
 | |
|         /// </summary>
 | |
|         internal MetadataWorkspace Metadata { get { return _tree.MetadataWorkspace; } }
 | |
|                 
 | |
|         /// <summary>
 | |
|         /// Gets a DbExpression representing the query that should be spanned.
 | |
|         /// </summary>
 | |
|         internal DbExpression Query { get { return _toRewrite; } }
 | |
|         
 | |
|         /// <summary>
 | |
|         /// Gets a value indicating whether relationship span is required (ObjectQuery sets this to 'false' for NoTracking queries).
 | |
|         /// </summary>
 | |
|         internal bool RelationshipSpan { get { return _relationshipSpan; } set { _relationshipSpan = value; } }
 | |
|         
 | |
|         /// <summary>
 | |
|         /// Gets a dictionary that indicates, for a given result row type produced by a span rewrite, 
 | |
|         /// which columns represent which association end members.
 | |
|         /// This dictionary is initially empty before <see cref="RewriteQuery"/> is called and will remain so
 | |
|         /// if no rewrites are required.
 | |
|         /// </summary>
 | |
|         internal SpanIndex SpanIndex { get { return _spanIndex; } }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Main 'public' entry point called by ObjectQuery.
 | |
|         /// </summary>
 | |
|         /// <returns>The rewritten version of <see cref="Query"/> if spanning was required; otherwise <c>null</c>.</returns>
 | |
|         internal DbExpression RewriteQuery()
 | |
|         {
 | |
|             DbExpression retExpr = Rewrite(_toRewrite);
 | |
|             if (object.ReferenceEquals(_toRewrite, retExpr))
 | |
|             {
 | |
|                 return null;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 return retExpr;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         #endregion
 | |
| 
 | |
|         #region 'Protected' API
 | |
|         
 | |
|         internal struct SpanTrackingInfo
 | |
|         {
 | |
|             public List<KeyValuePair<string, DbExpression>> ColumnDefinitions;
 | |
|             public AliasGenerator ColumnNames;
 | |
|             public Dictionary<int, AssociationEndMember> SpannedColumns;
 | |
|             public Dictionary<AssociationEndMember, bool> FullSpannedEnds;
 | |
|         }
 | |
| 
 | |
|         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 virtual SpanTrackingInfo CreateEntitySpanTrackingInfo(DbExpression expression, EntityType entityType) { return new SpanTrackingInfo(); }
 | |
|         
 | |
|         protected DbExpression Rewrite(DbExpression expression)
 | |
|         {
 | |
|             //SQLBUDT #554182: This is special casing for expressions below which it is safe to push the span
 | |
|             // info without having to rebind.  By pushing the span info down (i.e. possible extra projections),
 | |
|             // we potentially end up with simpler generated command. 
 | |
|             switch(expression.ExpressionKind)
 | |
|             {
 | |
|                 case DbExpressionKind.Element:
 | |
|                     return RewriteElementExpression((DbElementExpression)expression);
 | |
|                 case DbExpressionKind.Limit:
 | |
|                     return RewriteLimitExpression((DbLimitExpression)expression);
 | |
|             }
 | |
| 
 | |
|             switch(expression.ResultType.EdmType.BuiltInTypeKind)
 | |
|             {
 | |
|                 case BuiltInTypeKind.EntityType:
 | |
|                     return RewriteEntity(expression, (EntityType)expression.ResultType.EdmType);
 | |
| 
 | |
|                 case BuiltInTypeKind.CollectionType:
 | |
|                     return RewriteCollection(expression, (CollectionType)expression.ResultType.EdmType);
 | |
| 
 | |
|                 case BuiltInTypeKind.RowType:
 | |
|                     return RewriteRow(expression, (RowType)expression.ResultType.EdmType);
 | |
| 
 | |
|                 default:
 | |
|                     return expression;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         #endregion
 | |
| 
 | |
|         private void AddSpannedRowType(RowType spannedType, TypeUsage originalType)
 | |
|         {
 | |
|             if (null == _spanIndex)
 | |
|             {
 | |
|                 _spanIndex = new SpanIndex();
 | |
|             }
 | |
| 
 | |
|             _spanIndex.AddSpannedRowType(spannedType, originalType);
 | |
|         }
 | |
| 
 | |
|         private void AddSpanMap(RowType rowType, Dictionary<int, AssociationEndMember> columnMap)
 | |
|         {
 | |
|             if (null == _spanIndex)
 | |
|             {
 | |
|                 _spanIndex = new SpanIndex();
 | |
|             }
 | |
| 
 | |
|             _spanIndex.AddSpanMap(rowType, columnMap);
 | |
|         }
 | |
| 
 | |
|         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;
 | |
|         }
 | |
| 
 | |
|         private DbExpression RewriteElementExpression(DbElementExpression expression)
 | |
|         {
 | |
|             DbExpression rewrittenInput = Rewrite(expression.Argument);
 | |
|             if (!object.ReferenceEquals(expression.Argument, rewrittenInput))
 | |
|             {
 | |
|                 expression = rewrittenInput.Element();
 | |
|             }
 | |
|             return expression;
 | |
|         }
 | |
| 
 | |
|         private DbExpression RewriteLimitExpression(DbLimitExpression expression)
 | |
|         {
 | |
|             DbExpression rewrittenInput = Rewrite(expression.Argument);
 | |
|             if (!object.ReferenceEquals(expression.Argument, rewrittenInput))
 | |
|             {
 | |
|                 // Note that here we use the original expression.Limit. It is safe to do so, 
 | |
|                 //  because we only allow physical paging (i.e. Limit can only be a constant or parameter)
 | |
|                 expression = rewrittenInput.Limit(expression.Limit);
 | |
|             }
 | |
|             return expression;
 | |
|         }
 | |
| 
 | |
|         private DbExpression RewriteRow(DbExpression expression, RowType rowType)
 | |
|         {
 | |
|             DbLambdaExpression lambdaExpression = expression as DbLambdaExpression;
 | |
|             DbNewInstanceExpression newRow;
 | |
| 
 | |
|             if (lambdaExpression != null)
 | |
|             {
 | |
|                 // NOTE: We rely on the fact that today span cannot be done over queries containing DbLambdaExpressions
 | |
|                 // created by users, because user-created expressions cannot be used for querying in O-space.
 | |
|                 // If that were to change, pushing span beyond a LambdaExpression could cause variable name 
 | |
|                 // collisions between the variable names used in the Lambda and the names generated by the
 | |
|                 // RelationshipNavigationVisitor.           
 | |
|                 newRow = lambdaExpression.Lambda.Body as DbNewInstanceExpression;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 newRow = expression as DbNewInstanceExpression;
 | |
|             }
 | |
| 
 | |
|             Dictionary<int, DbExpression> unmodifiedColumns = null;
 | |
|             Dictionary<int, DbExpression> spannedColumns = null;
 | |
|             for(int idx = 0; idx < rowType.Properties.Count; idx++)
 | |
|             {
 | |
|                 // Retrieve the property that represents the current column
 | |
|                 EdmProperty columnProp = rowType.Properties[idx];
 | |
| 
 | |
|                 // Construct an expression that defines the current column.
 | |
|                 DbExpression columnExpr = null;
 | |
|                 if(newRow != null)
 | |
|                 {
 | |
|                     // For a row-constructing NewInstance expression, the corresponding argument can simply be used
 | |
|                     columnExpr = newRow.Arguments[idx];
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     // For all other expressions the property corresponding to the column name must be retrieved
 | |
|                     // from the row-typed expression
 | |
|                     columnExpr = expression.Property(columnProp.Name);
 | |
|                 }
 | |
| 
 | |
|                 DbExpression spannedColumn = this.Rewrite(columnExpr);
 | |
|                 if (!object.ReferenceEquals(spannedColumn, columnExpr))
 | |
|                 {
 | |
|                     // If so, then update the dictionary of column index to span information
 | |
|                     if (null == spannedColumns)
 | |
|                     {
 | |
|                         spannedColumns = new Dictionary<int, DbExpression>();
 | |
|                     }
 | |
| 
 | |
|                     spannedColumns[idx] = spannedColumn;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     // Otherwise, update the dictionary of column index to unmodified expression
 | |
|                     if(null == unmodifiedColumns)
 | |
|                     {
 | |
|                         unmodifiedColumns = new Dictionary<int, DbExpression>();
 | |
|                     }
 | |
| 
 | |
|                     unmodifiedColumns[idx] = columnExpr;
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             // A new expression need only be built if at least one column was spanned
 | |
|             if(null == spannedColumns)
 | |
|             {
 | |
|                 // No columns were spanned, indicate that the original expression should remain.
 | |
|                 return expression;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 // At least one column was spanned, so build a new row constructor that defines the new row, including spanned columns.
 | |
|                 List<DbExpression> columnArguments = new List<DbExpression>(rowType.Properties.Count);
 | |
|                 List<EdmProperty> properties = new List<EdmProperty>(rowType.Properties.Count);
 | |
|                 for (int idx = 0; idx < rowType.Properties.Count; idx++)
 | |
|                 {
 | |
|                     EdmProperty columnProp = rowType.Properties[idx];
 | |
|                     DbExpression columnDef = null;
 | |
|                     if (!spannedColumns.TryGetValue(idx, out columnDef))
 | |
|                     {
 | |
|                         columnDef = unmodifiedColumns[idx];
 | |
|                     }
 | |
|                     columnArguments.Add(columnDef);
 | |
|                     properties.Add(new EdmProperty(columnProp.Name, columnDef.ResultType));
 | |
|                 }
 | |
| 
 | |
|                 // Copy over any eLinq initializer metadata (if present, or null if not).
 | |
|                 // Note that this initializer metadata does not strictly match the new row type
 | |
|                 // that includes spanned columns, but will be correct once the object materializer
 | |
|                 // has interpreted the query results to produce the correct value for each colum.
 | |
|                 RowType rewrittenRow = new RowType(properties, rowType.InitializerMetadata);
 | |
|                 TypeUsage rewrittenRowTypeUsage = TypeUsage.Create(rewrittenRow);
 | |
|                 DbExpression rewritten = rewrittenRowTypeUsage.New(columnArguments);
 | |
|                 
 | |
|                 // SQLBUDT #554182: If we insert a new projection we should should make sure to 
 | |
|                 // not interfere with the nullability of the input. 
 | |
|                 // In particular, if the input row is null and we construct a new row as a projection over its columns
 | |
|                 // we would get a row consisting of nulls, instead of a null row. 
 | |
|                 // Thus, given an input X, we rewritte it as:  if (X is null) then NULL else rewritten.
 | |
|                 if (newRow == null)
 | |
|                 {
 | |
|                     DbExpression condition = DbExpressionBuilder.CreateIsNullExpressionAllowingRowTypeArgument(expression);
 | |
|                     DbExpression nullExpression = DbExpressionBuilder.Null(rewrittenRowTypeUsage);
 | |
|                     rewritten = DbExpressionBuilder.Case(
 | |
|                         new List<DbExpression>(new DbExpression[] { condition }),
 | |
|                         new List<DbExpression>(new DbExpression[] { nullExpression }),
 | |
|                         rewritten);
 | |
|                 }
 | |
|                 
 | |
|                 // Add an entry to the spanned row type => original row type map for the new row type.
 | |
|                 AddSpannedRowType(rewrittenRow, expression.ResultType);
 | |
|                 
 | |
|                 if (lambdaExpression != null && newRow != null)
 | |
|                 {
 | |
|                     rewritten = DbLambda.Create(rewritten, lambdaExpression.Lambda.Variables).Invoke(lambdaExpression.Arguments);
 | |
|                 }
 | |
| 
 | |
|                 return rewritten;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         private DbExpression RewriteCollection(DbExpression expression, CollectionType collectionType)
 | |
|         {
 | |
|             DbExpression target = expression;
 | |
| 
 | |
|             // If the collection expression is a project expression, get a strongly typed reference to it for later use.
 | |
|             DbProjectExpression project = null;
 | |
|             if (DbExpressionKind.Project == expression.ExpressionKind)
 | |
|             {
 | |
|                 project = (DbProjectExpression)expression;
 | |
|                 target = project.Input.Expression;
 | |
|             }
 | |
| 
 | |
|             // If Relationship span is enabled and the source of this collection is (directly or indirectly)
 | |
|             // a RelationshipNavigation operation, it may be possible to optimize the relationship span rewrite
 | |
|             // for the Entities produced by the navigation. 
 | |
|             NavigationInfo navInfo = null;
 | |
|             if (this.RelationshipSpan)
 | |
|             {
 | |
|                 // Attempt to find a RelationshipNavigationExpression in the collection-defining expression
 | |
|                 target = RelationshipNavigationVisitor.FindNavigationExpression(target, _aliasGenerator, out navInfo);
 | |
|             }
 | |
| 
 | |
|             // If a relationship navigation expression defines this collection, make the Ref that is the navigation source
 | |
|             // and the source association end available for possible use when the projection over the collection is rewritten.
 | |
|             if (navInfo != null)
 | |
|             {
 | |
|                 this.EnterNavigationCollection(navInfo);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 // Otherwise, add a null navigation info instance to the stack to indicate that relationship navigation
 | |
|                 // cannot be optimized for the entities produced by this collection expression (if it is a collection of entities).
 | |
|                 this.EnterCollection();
 | |
|             }
 | |
|             
 | |
|             // If the expression is already a DbProjectExpression then simply visit the projection,
 | |
|             // instead of introducing another projection over the existing one.
 | |
|             DbExpression result = expression;
 | |
|             if (project != null)
 | |
|             {
 | |
|                 DbExpression newProjection = this.Rewrite(project.Projection);
 | |
|                 if (!object.ReferenceEquals(project.Projection, newProjection))
 | |
|                 {
 | |
|                     result = target.BindAs(project.Input.VariableName).Project(newProjection);
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 // This is not a recognized special case, so simply add the span projection over the original
 | |
|                 // collection-producing expression, if it is required.
 | |
|                 DbExpressionBinding collectionBinding = target.BindAs(_aliasGenerator.Next());
 | |
|                 DbExpression projection = collectionBinding.Variable;
 | |
| 
 | |
|                 DbExpression spannedProjection = this.Rewrite(projection);
 | |
| 
 | |
|                 if (!object.ReferenceEquals(projection, spannedProjection))
 | |
|                 {
 | |
|                     result = collectionBinding.Project(spannedProjection);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Remove any navigation information from scope, if it was added
 | |
|             this.ExitCollection();
 | |
| 
 | |
|             // If a navigation expression defines this collection and its navigation information was used to
 | |
|             // short-circuit relationship span rewrites, then enclose the entire rewritten expression in a
 | |
|             // Lambda binding that brings the source Ref of the navigation operation into scope. This ref is
 | |
|             // refered to by VariableReferenceExpressions in the original navigation expression as well as any
 | |
|             // short-circuited relationship span columns in the rewritten expression.
 | |
|             if (navInfo != null && navInfo.InUse)
 | |
|             {
 | |
|                 // Create a Lambda function that binds the original navigation source expression under the variable name
 | |
|                 // used in the navigation expression and the relationship span columns, and which has its Lambda body
 | |
|                 // defined by the rewritten collection expression.
 | |
|                 List<DbVariableReferenceExpression> formals = new List<DbVariableReferenceExpression>(1);
 | |
|                 formals.Add(navInfo.SourceVariable);
 | |
| 
 | |
|                 List<DbExpression> args = new List<DbExpression>(1);
 | |
|                 args.Add(navInfo.Source);
 | |
| 
 | |
|                 result = DbExpressionBuilder.Invoke(DbExpressionBuilder.Lambda(result, formals), args);
 | |
|             }
 | |
| 
 | |
|             // Return the (possibly rewritten) collection expression.
 | |
|             return result;
 | |
|         }
 | |
|         
 | |
|         private void EnterCollection()
 | |
|         {
 | |
|             _navSources.Push(null);
 | |
|         }
 | |
| 
 | |
|         private void EnterNavigationCollection(NavigationInfo info)
 | |
|         {
 | |
|             _navSources.Push(info);
 | |
|         }
 | |
| 
 | |
|         private void ExitCollection()
 | |
|         {
 | |
|             _navSources.Pop();
 | |
|         }
 | |
| 
 | |
|         private bool TryGetNavigationSource(AssociationEndMember wasSourceNowTargetEnd, out DbExpression source)
 | |
|         {
 | |
|             source = null;
 | |
| 
 | |
|             NavigationInfo info = null;
 | |
|             if (_navSources.Count > 0)
 | |
|             {
 | |
|                 info = _navSources.Peek();
 | |
|                 if (info != null && !object.ReferenceEquals(wasSourceNowTargetEnd, info.SourceEnd))
 | |
|                 {
 | |
|                     info = null;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (info != null)
 | |
|             {
 | |
|                 source = info.SourceVariable;
 | |
|                 info.InUse = true;
 | |
|                 return true;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
|                       
 | |
|         /// <summary>
 | |
|         /// Gathers the applicable { from, to } relationship end pairings for the specified entity type.
 | |
|         /// Note that it is possible for both { x, y } and { y, x } - where x and y are relationship ends - 
 | |
|         /// to be returned if the relationship is symmetric (in the sense that it has multiplicity of at
 | |
|         /// most one in each direction and the type of each end is Ref to the same Entity type, or a supertype).
 | |
|         /// </summary>
 | |
|         /// <param name="entityType">The Entity type for which the applicable { from, to } end pairings should be retrieved.</param>
 | |
|         /// <returns>
 | |
|         ///     A List of association end members pairings that describes the available { from, to } navigations
 | |
|         ///     for the specified Entity type that are valid for Relationship Span; or <c>null</c> if no such pairings exist.
 | |
|         /// </returns>
 | |
|         private List<KeyValuePair<AssociationEndMember, AssociationEndMember>> GetRelationshipSpanEnds(EntityType entityType)
 | |
|         {
 | |
|             // The list to be returned; initially null.
 | |
|             List<KeyValuePair<AssociationEndMember, AssociationEndMember>> retList = null;
 | |
| 
 | |
|             // If relationship span is not enabled then do not attempt to retrieve the applicable navigations.
 | |
|             if (_relationshipSpan)
 | |
|             {
 | |
|                 // Consider all Association types...
 | |
|                 foreach (AssociationType association in _tree.MetadataWorkspace.GetItems<AssociationType>(DataSpace.CSpace))
 | |
|                 {
 | |
|                     // ... which have exactly two ends
 | |
|                     if (2 == association.AssociationEndMembers.Count)
 | |
|                     {
 | |
|                         AssociationEndMember end0 = association.AssociationEndMembers[0];
 | |
|                         AssociationEndMember end1 = association.AssociationEndMembers[1];
 | |
| 
 | |
|                         // If end0 -> end1 is valid for relationship span then add { end0, end1 }
 | |
|                         // to the list of end pairings.
 | |
|                         if (IsValidRelationshipSpan(entityType, association, end0, end1))
 | |
|                         {
 | |
|                             // If the list has not been instantiated, do so now.
 | |
|                             if (null == retList)
 | |
|                             {
 | |
|                                 retList = new List<KeyValuePair<AssociationEndMember, AssociationEndMember>>();
 | |
|                             }
 | |
| 
 | |
|                             retList.Add(new KeyValuePair<AssociationEndMember, AssociationEndMember>(end0, end1));
 | |
|                         }
 | |
| 
 | |
|                         // Similarly if the inverse navigation is also or instead valid for relationship span
 | |
|                         // then add the { end1, end0 } pairing to the list of valid end pairings.
 | |
|                         if (IsValidRelationshipSpan(entityType, association, end1, end0))
 | |
|                         {
 | |
|                             // Again, if the list has not been instantiated, do so now.
 | |
|                             if (null == retList)
 | |
|                             {
 | |
|                                 retList = new List<KeyValuePair<AssociationEndMember, AssociationEndMember>>();
 | |
|                             }
 | |
| 
 | |
|                             retList.Add(new KeyValuePair<AssociationEndMember, AssociationEndMember>(end1, end0));
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Return the list (which may still be null at this point)
 | |
|             return retList;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Determines whether the specified { from, to } relationship end pairing represents a navigation that is
 | |
|         /// valid for a relationship span sourced by an instance of the specified entity type.
 | |
|         /// </summary>
 | |
|         /// <param name="compareType">The Entity type which valid 'from' ends must reference (or a supertype of that Entity type)</param>
 | |
|         /// <param name="associationType">The Association type to consider.</param>
 | |
|         /// <param name="fromEnd">The candidate 'from' end, which will be checked based on the Entity type it references</param>
 | |
|         /// <param name="toEnd">The candidate 'to' end, which will be checked base on the upper bound of its multiplicity</param>
 | |
|         /// <returns>
 | |
|         ///     <c>True</c> if the end pairing represents a valid navigation from an instance of the specified entity type
 | |
|         ///     to an association end with a multiplicity upper bound of at most 1; otherwise <c>false</c>
 | |
|         /// </returns>
 | |
|         private static bool IsValidRelationshipSpan(EntityType compareType, AssociationType associationType, AssociationEndMember fromEnd, AssociationEndMember toEnd)
 | |
|         {
 | |
|             // Only a relationship end with a multiplicity of AT MOST one may be
 | |
|             // considered as the 'to' end, so that the cardinality of the result
 | |
|             // of the relationship span has an upper bound of 1. 
 | |
|             // Therefore ends with RelationshipMultiplicity of EITHER One OR ZeroOrOne
 | |
|             // are the only ends that should be considered as target ends.
 | |
|             // Note that a relationship span can be sourced by an Entity that is of the same type
 | |
|             // as the Entity type referenced by the 'from' end OR any type in the same branch of 
 | |
|             // the type hierarchy.
 | |
|             //
 | |
|             // For example, in the following hierarchy:
 | |
|             //
 | |
|             // A  (*<-->?) AOwner
 | |
|             // |_B  (*<-->1) BOwner
 | |
|             // |_A1  (*<-->?) A1Owner
 | |
|             //   |_A2
 | |
|             //     |_A3_1  (1<-->?) A3_1Owner
 | |
|             //     |_A3_2  (*<-->1) A3_2Owner
 | |
|             //
 | |
|             // An instance of 'A' would need ALL the 'AOwner', 'BOwner', 'A1Owner', 'A3_1Owner' and 'A3_2Owner' ends
 | |
|             // spanned in because an instance of 'A' could actually be an instance of A, B, A1, A2, A3_1 or A3_2. 
 | |
|             // An instance of 'B' would only need 'AOwner' and 'BOwner' spanned in.
 | |
|             // An instance of A2 would need 'AOwner', 'A1Owner', 'A3_1Owner' and 'A3_2Owner' spanned in.
 | |
|             // An instance of A3_1 would only need 'AOwner', 'A1Owner' and 'A3_1Owner' spanned in.
 | |
|             //
 | |
|             // In general, the rule for relationship span is:
 | |
|             // - 'To' end cardinality AT MOST one
 | |
|             //   AND
 | |
|             //   - Referenced Entity type of 'From' end is equal to instance Entity type
 | |
|             //     OR
 | |
|             //   - Referenced Entity type of 'From' end is a supertype of instance Entity type
 | |
|             //     OR
 | |
|             //   - Referenced Entity type of 'From' end is a subtype of instance Entity type
 | |
|             //     (this follows from the fact that an instance of 'A' may be an instance of any of its derived types.
 | |
|             //      Navigation for a subtype relationship will return null if the Entity instance navigation source
 | |
|             //      is not actually of the required subtype).
 | |
|             //
 | |
|             if(!associationType.IsForeignKey &&
 | |
|                (RelationshipMultiplicity.One == toEnd.RelationshipMultiplicity ||
 | |
|                 RelationshipMultiplicity.ZeroOrOne == toEnd.RelationshipMultiplicity))
 | |
|             {
 | |
|                 EntityType fromEntityType = (EntityType)((RefType)fromEnd.TypeUsage.EdmType).ElementType;
 | |
|                 return (ObjectSpanRewriter.EntityTypeEquals(compareType, fromEntityType) ||
 | |
|                         TypeSemantics.IsSubTypeOf(compareType, fromEntityType) ||
 | |
|                         TypeSemantics.IsSubTypeOf(fromEntityType, compareType));
 | |
|             }
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         #region Nested types used for Relationship span over Relationship Navigation optimizations
 | |
| 
 | |
|         private class NavigationInfo
 | |
|         {
 | |
|             private readonly DbRelationshipNavigationExpression _original;
 | |
|             private readonly DbRelationshipNavigationExpression _rewritten;
 | |
|             private DbVariableReferenceExpression _sourceRef;
 | |
|             private AssociationEndMember _sourceEnd;
 | |
|             private DbExpression _source;            
 | |
| 
 | |
|             public NavigationInfo(DbRelationshipNavigationExpression originalNavigation, DbRelationshipNavigationExpression rewrittenNavigation)
 | |
|             {
 | |
|                 Debug.Assert(originalNavigation != null, "originalNavigation cannot be null");
 | |
|                 Debug.Assert(rewrittenNavigation != null, "rewrittenNavigation cannot be null");
 | |
| 
 | |
|                 this._original = originalNavigation;
 | |
|                 this._rewritten = rewrittenNavigation;
 | |
|                 this._sourceEnd = (AssociationEndMember)originalNavigation.NavigateFrom;
 | |
|                 this._sourceRef = (DbVariableReferenceExpression)rewrittenNavigation.NavigationSource;
 | |
|                 this._source = originalNavigation.NavigationSource;
 | |
|             }
 | |
| 
 | |
|             public bool InUse;
 | |
| 
 | |
|             public AssociationEndMember SourceEnd { get { return _sourceEnd; } }
 | |
|             public DbExpression Source { get { return _source; } }
 | |
|             public DbVariableReferenceExpression SourceVariable { get { return _sourceRef; } }
 | |
|         }
 | |
| 
 | |
|         private class RelationshipNavigationVisitor : DefaultExpressionVisitor
 | |
|         {
 | |
|             internal static DbExpression FindNavigationExpression(DbExpression expression, AliasGenerator aliasGenerator, out NavigationInfo navInfo)
 | |
|             {
 | |
|                 Debug.Assert(TypeSemantics.IsCollectionType(expression.ResultType), "Non-collection input to projection?");
 | |
| 
 | |
|                 navInfo = null;
 | |
| 
 | |
|                 TypeUsage elementType = ((CollectionType)expression.ResultType.EdmType).TypeUsage;
 | |
|                 if (!TypeSemantics.IsEntityType(elementType) && !TypeSemantics.IsReferenceType(elementType))
 | |
|                 {
 | |
|                     return expression;
 | |
|                 }
 | |
| 
 | |
|                 RelationshipNavigationVisitor visitor = new RelationshipNavigationVisitor(aliasGenerator);
 | |
|                 DbExpression rewrittenExpression = visitor.Find(expression);
 | |
|                 if (!object.ReferenceEquals(expression, rewrittenExpression))
 | |
|                 {
 | |
|                     Debug.Assert(visitor._original != null && visitor._rewritten != null, "Expression was rewritten but no navigation was found?");
 | |
|                     navInfo = new NavigationInfo(visitor._original, visitor._rewritten);
 | |
|                     return rewrittenExpression;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     return expression;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             private readonly AliasGenerator _aliasGenerator;
 | |
|             private DbRelationshipNavigationExpression _original;
 | |
|             private DbRelationshipNavigationExpression _rewritten;
 | |
| 
 | |
|             private RelationshipNavigationVisitor(AliasGenerator aliasGenerator)
 | |
|             {
 | |
|                 _aliasGenerator = aliasGenerator;
 | |
|             }
 | |
| 
 | |
|             private DbExpression Find(DbExpression expression)
 | |
|             {
 | |
|                 return this.VisitExpression(expression);
 | |
|             }
 | |
| 
 | |
|             protected override DbExpression VisitExpression(DbExpression expression)
 | |
|             {
 | |
|                 switch (expression.ExpressionKind)
 | |
|                 {
 | |
|                     case DbExpressionKind.RelationshipNavigation:
 | |
|                     case DbExpressionKind.Distinct:
 | |
|                     case DbExpressionKind.Filter:
 | |
|                     case DbExpressionKind.Limit:
 | |
|                     case DbExpressionKind.OfType:
 | |
|                     case DbExpressionKind.Project:
 | |
|                     case DbExpressionKind.Sort:
 | |
|                     case DbExpressionKind.Skip:
 | |
|                         return base.VisitExpression(expression);
 | |
| 
 | |
|                     default:
 | |
|                         return expression;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             public override DbExpression Visit(DbRelationshipNavigationExpression expression)
 | |
|             {
 | |
|                 this._original = expression;
 | |
| 
 | |
|                 // Ensure a unique variable name when the expression is used in a command tree
 | |
|                 string varName = _aliasGenerator.Next();
 | |
|                 DbVariableReferenceExpression sourceRef = new DbVariableReferenceExpression(expression.NavigationSource.ResultType, varName);
 | |
| 
 | |
|                 this._rewritten = sourceRef.Navigate(expression.NavigateFrom, expression.NavigateTo);
 | |
| 
 | |
|                 return this._rewritten;
 | |
|             }
 | |
| 
 | |
|             // For Distinct, Limit, OfType there is no need to override the base visitor behavior.
 | |
|             
 | |
|             public override DbExpression Visit(DbFilterExpression expression)
 | |
|             {
 | |
|                 // Only consider the Filter input
 | |
|                 DbExpression found = Find(expression.Input.Expression);
 | |
|                 if(!object.ReferenceEquals(found, expression.Input.Expression))
 | |
|                 {
 | |
|                     return found.BindAs(expression.Input.VariableName).Filter(expression.Predicate);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     return expression;
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             public override DbExpression Visit(DbProjectExpression expression)
 | |
|             {
 | |
|                 // Only allowed cases:
 | |
|                 // SELECT Deref(x) FROM <expression> AS x
 | |
|                 // SELECT x FROM <expression> as x
 | |
|                 DbExpression testExpr = expression.Projection;
 | |
|                 if (DbExpressionKind.Deref == testExpr.ExpressionKind)
 | |
|                 {
 | |
|                     testExpr = ((DbDerefExpression)testExpr).Argument;
 | |
|                 }
 | |
| 
 | |
|                 if (DbExpressionKind.VariableReference == testExpr.ExpressionKind)
 | |
|                 {
 | |
|                     DbVariableReferenceExpression varRef = (DbVariableReferenceExpression)testExpr;
 | |
|                     if (varRef.VariableName.Equals(expression.Input.VariableName, StringComparison.Ordinal))
 | |
|                     {
 | |
|                         DbExpression found = Find(expression.Input.Expression);
 | |
|                         if (!object.ReferenceEquals(found, expression.Input.Expression))
 | |
|                         {
 | |
|                             return found.BindAs(expression.Input.VariableName).Project(expression.Projection);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 return expression;
 | |
|             }
 | |
| 
 | |
|             public override DbExpression Visit(DbSortExpression expression)
 | |
|             {
 | |
|                 DbExpression found = Find(expression.Input.Expression);
 | |
|                 if(!object.ReferenceEquals(found, expression.Input.Expression))
 | |
|                 {
 | |
|                     return found.BindAs(expression.Input.VariableName).Sort(expression.SortOrder);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     return expression;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             public override DbExpression Visit(DbSkipExpression expression)
 | |
|             {
 | |
|                 DbExpression found = Find(expression.Input.Expression);
 | |
|                 if (!object.ReferenceEquals(found, expression.Input.Expression))
 | |
|                 {
 | |
|                     return found.BindAs(expression.Input.VariableName).Skip(expression.SortOrder, expression.Count);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     return expression;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         #endregion
 | |
|     }
 | |
| }
 |