/// <summary> /// Add the rel property induced by the specified relationship, (if the target /// end has a multiplicity of one) /// We only keep track of rel-properties that are "interesting" /// </summary> /// <param name="associationType">the association relationship</param> /// <param name="fromEnd">source end of the relationship traversal</param> /// <param name="toEnd">target end of the traversal</param> private void AddRelProperty( AssociationType associationType, AssociationEndMember fromEnd, AssociationEndMember toEnd) { if (toEnd.RelationshipMultiplicity == RelationshipMultiplicity.Many) { return; } var prop = new RelProperty(associationType, fromEnd, toEnd); if (_interestingRelProperties == null || !_interestingRelProperties.Contains(prop)) { return; } var entityType = ((RefType)fromEnd.TypeUsage.EdmType).ElementType; List<RelProperty> propList; if (!_relPropertyMap.TryGetValue(entityType, out propList)) { propList = new List<RelProperty>(); _relPropertyMap[entityType] = propList; } propList.Add(prop); }
/// <summary> /// Add the rel property induced by the specified relationship, (if the target /// end has a multiplicity of one) /// We only keep track of rel-properties that are "interesting" /// </summary> /// <param name="associationType">the association relationship</param> /// <param name="fromEnd">source end of the relationship traversal</param> /// <param name="toEnd">target end of the traversal</param> private void AddRelProperty(AssociationType associationType, AssociationEndMember fromEnd, AssociationEndMember toEnd) { if (toEnd.RelationshipMultiplicity == RelationshipMultiplicity.Many) { return; } RelProperty prop = new RelProperty(associationType, fromEnd, toEnd); if (_interestingRelProperties == null || !_interestingRelProperties.Contains(prop)) { return; } EntityTypeBase entityType = (EntityTypeBase)((RefType)fromEnd.TypeUsage.EdmType).ElementType; List <RelProperty> propList; if (!_relPropertyMap.TryGetValue(entityType, out propList)) { propList = new List <RelProperty>(); _relPropertyMap[entityType] = propList; } propList.Add(prop); }
/// <summary> /// Our definition of equality /// </summary> /// <param name="obj"></param> /// <returns></returns> public override bool Equals(object obj) { RelProperty other = obj as RelProperty; return(other != null && this.Relationship.EdmEquals(other.Relationship) && this.FromEnd.EdmEquals(other.FromEnd) && this.ToEnd.EdmEquals(other.ToEnd)); }
private Node RewriteFromOneNavigationProperty( RelProperty relProperty, List<RelationshipSet> relationshipSets, Node sourceRefNode, out Var outputVar) { PlanCompiler.Assert(relationshipSets.Count > 0, "expected at least one relationship set here"); PlanCompiler.Assert( relProperty.FromEnd.RelationshipMultiplicity != RelationshipMultiplicity.Many, "Expected source end multiplicity to be one. Found 'Many' instead " + relProperty); var entityType = TypeHelpers.GetElementTypeUsage(relProperty.ToEnd.TypeUsage); var scanTableNodes = new List<Node>(relationshipSets.Count); var scanTableVars = new List<Var>(relationshipSets.Count); foreach (var r in relationshipSets) { var entitySet = FindTargetEntitySet(r, relProperty.ToEnd); Var tableVar; var tableNode = BuildOfTypeTable(entitySet, entityType, out tableVar); scanTableNodes.Add(tableNode); scanTableVars.Add(tableVar); } // // Build the union-all node // Node unionAllNode; m_command.BuildUnionAllLadder(scanTableNodes, scanTableVars, out unionAllNode, out outputVar); // // Now build up the appropriate filter. Select out the relproperty from the other end // var inverseRelProperty = new RelProperty(relProperty.Relationship, relProperty.ToEnd, relProperty.FromEnd); PlanCompiler.Assert( m_command.IsRelPropertyReferenced(inverseRelProperty), "Unreferenced rel property? " + inverseRelProperty); var inverseRelPropertyNode = m_command.CreateNode( m_command.CreateRelPropertyOp(inverseRelProperty), m_command.CreateNode(m_command.CreateVarRefOp(outputVar))); var predicateNode = m_command.BuildComparison( OpType.EQ, sourceRefNode, inverseRelPropertyNode); var ret = m_command.CreateNode(m_command.CreateFilterOp(), unionAllNode, predicateNode); return ret; }
/// <summary> /// Rewrite a navigation property when the target end has multiplicity /// of one (or zero..one) and the source end has multiplicity of one (or zero..one). /// /// <see cref="RewriteFromOneNavigationProperty"/> /// We add the translation as a subquery to the parent rel op and return a reference to /// the corresponding var /// </summary> /// <param name="relProperty">the rel-property describing the relationship traversal</param> /// <param name="relationshipSets">the list of relevant relationshipsets</param> /// <param name="sourceRefNode">node tree corresponding to the source entity ref</param> /// <returns>the rewritten subtree</returns> private Node RewriteOneToOneNavigationProperty( RelProperty relProperty, List<RelationshipSet> relationshipSets, Node sourceRefNode) { Var outputVar; var ret = RewriteFromOneNavigationProperty(relProperty, relationshipSets, sourceRefNode, out outputVar); ret = VisitNode(ret); ret = AddSubqueryToParentRelOp(outputVar, ret); return ret; }
/// <summary> /// Rewrite a navigation property when the source end has multiplicity /// of one (or zero..one) and the target end has multiplicity of many. /// /// <see cref="RewriteFromOneNavigationProperty"/> /// We also build out a CollectOp over the subquery above, and return that /// </summary> /// <param name="relProperty">the rel-property describing the relationship traversal</param> /// <param name="relationshipSets">the list of relevant relationshipsets</param> /// <param name="sourceRefNode">node tree corresponding to the source entity ref</param> /// <returns>the rewritten subtree</returns> private Node RewriteOneToManyNavigationProperty( RelProperty relProperty, List<RelationshipSet> relationshipSets, Node sourceRefNode) { Var outputVar; var ret = RewriteFromOneNavigationProperty(relProperty, relationshipSets, sourceRefNode, out outputVar); // The return value is a collection, but used as a property, thus it needs to be capped with a collect ret = m_command.BuildCollect(ret, outputVar); return ret; }
/// <summary> /// Rewrite a navigation property when the target end has multiplicity /// of one (or zero..one) and the source end has multiplicity of many. /// /// Note that this translation is also valid for a navigation property when the target /// end has multiplicity of one (or zero..one) and the source end has multiplicity of one /// (or zero..one), but a different translation is used because it yields a simpler query in some cases. /// /// We simply pick up the corresponding rel property from the input entity, and /// apply a deref operation /// NavProperty(e, n) => deref(relproperty(e, r)) /// where e is the entity expression, n is the nav-property, and r is the corresponding /// rel-property /// </summary> /// <param name="relProperty">the rel-property describing the navigation</param> /// <param name="sourceEntityNode">entity instance that we're starting the traversal from</param> /// <param name="resultType">type of the target entity</param> /// <returns>a rewritten subtree</returns> private Node RewriteManyToOneNavigationProperty( RelProperty relProperty, Node sourceEntityNode, TypeUsage resultType) { var relPropertyOp = m_command.CreateRelPropertyOp(relProperty); var relPropertyNode = m_command.CreateNode(relPropertyOp, sourceEntityNode); var derefOp = m_command.CreateDerefOp(resultType); var derefNode = m_command.CreateNode(derefOp, relPropertyNode); return derefNode; }
private Node BuildRelPropertyExpression( EntitySetBase entitySet, RelProperty relProperty, Node keyExpr) { // // Make a copy of the current key expression // keyExpr = OpCopier.Copy(m_command, keyExpr); // // Find the relationship set corresponding to this entityset (and relProperty) // Return a null ref, if we can't find one // var relSet = FindRelationshipSet(entitySet, relProperty); if (relSet == null) { return m_command.CreateNode(m_command.CreateNullOp(relProperty.ToEnd.TypeUsage)); } var scanTableOp = m_command.CreateScanTableOp(Command.CreateTableDefinition(relSet)); PlanCompiler.Assert( scanTableOp.Table.Columns.Count == 1, "Unexpected column count for table:" + scanTableOp.Table.TableMetadata.Extent + "=" + scanTableOp.Table.Columns.Count); var scanTableVar = scanTableOp.Table.Columns[0]; var scanNode = m_command.CreateNode(scanTableOp); var sourceEndNode = m_command.CreateNode( m_command.CreatePropertyOp(relProperty.FromEnd), m_command.CreateNode(m_command.CreateVarRefOp(scanTableVar))); var predicateNode = m_command.BuildComparison( OpType.EQ, keyExpr, m_command.CreateNode(m_command.CreateGetRefKeyOp(keyExpr.Op.Type), sourceEndNode)); var filterNode = m_command.CreateNode( m_command.CreateFilterOp(), scanNode, predicateNode); // // Process the node, and then add this as a subquery to the parent relop // var ret = VisitNode(filterNode); ret = AddSubqueryToParentRelOp(scanTableVar, ret); // // Now extract out the target end property // ret = m_command.CreateNode( m_command.CreatePropertyOp(relProperty.ToEnd), ret); return ret; }
private static bool TryMatchEntityTypeConstructor( DbExpression then, Dictionary<EdmProperty, DbExpression> propertyMap, Dictionary<RelProperty, DbExpression> relPropertyMap, Dictionary<EntityType, List<RelProperty>> typeToRelPropertyMap, out EntityType entityType) { if (then.ExpressionKind != DbExpressionKind.NewInstance) { entityType = null; return false; } var constructor = (DbNewInstanceExpression)then; entityType = (EntityType)constructor.ResultType.EdmType; // process arguments to constructor (must be aligned across all case statements) Debug.Assert(entityType.Properties.Count == constructor.Arguments.Count, "invalid new instance"); for (var j = 0; j < entityType.Properties.Count; j++) { var property = entityType.Properties[j]; var assignment = constructor.Arguments[j]; DbExpression existingAssignment; if (propertyMap.TryGetValue(property, out existingAssignment)) { if (!ExpressionsCompatible(assignment, existingAssignment)) { return false; } } else { propertyMap.Add(property, assignment); } } // Now handle the rel properties if (constructor.HasRelatedEntityReferences) { List<RelProperty> relPropertyList; if (!typeToRelPropertyMap.TryGetValue(entityType, out relPropertyList)) { relPropertyList = new List<RelProperty>(); typeToRelPropertyMap[entityType] = relPropertyList; } foreach (var relatedRef in constructor.RelatedEntityReferences) { var relProperty = new RelProperty( (RelationshipType)relatedRef.TargetEnd.DeclaringType, relatedRef.SourceEnd, relatedRef.TargetEnd); var assignment = relatedRef.TargetEntityReference; DbExpression existingAssignment; if (relPropertyMap.TryGetValue(relProperty, out existingAssignment)) { if (!ExpressionsCompatible(assignment, existingAssignment)) { return false; } } else { relPropertyMap.Add(relProperty, assignment); } relPropertyList.Add(relProperty); } } return true; }
/// <summary> /// Creates a new PropertyOp /// </summary> /// <param name="prop">EdmProperty metadata that specifies the property</param> /// <returns>A new PropertyOp that references the specified property metadata</returns> internal PropertyOp CreatePropertyOp(EdmMember prop) { // // Track all rel-properties // NavigationProperty navProp = prop as NavigationProperty; if (navProp != null) { RelProperty relProperty = new RelProperty(navProp.RelationshipType, navProp.FromEndMember, navProp.ToEndMember); AddRelPropertyReference(relProperty); RelProperty inverseRelProperty = new RelProperty(navProp.RelationshipType, navProp.ToEndMember, navProp.FromEndMember); AddRelPropertyReference(inverseRelProperty); } // Actually create the propertyOp return new PropertyOp(Helper.GetModelTypeUsage(prop), prop); }
/// <summary> /// Is this rel-property referenced in the query so far /// </summary> /// <param name="relProperty">the rel-property</param> /// <returns>true, if the rel property was referenced in the query</returns> internal bool IsRelPropertyReferenced(RelProperty relProperty) { bool ret = m_referencedRelProperties.Contains(relProperty); return ret; }
/// <summary> /// Mark this rel-property as "referenced" in the current query, if the target /// end has multiplicity of one (or zero_or_one) /// </summary> /// <param name="relProperty">the rel-property</param> private void AddRelPropertyReference(RelProperty relProperty) { if (relProperty.ToEnd.RelationshipMultiplicity != RelationshipMultiplicity.Many && !m_referencedRelProperties.Contains(relProperty)) { m_referencedRelProperties.Add(relProperty); } }
/// <summary> /// Create a new NavigateOp node /// </summary> /// <param name="type">the output type of the navigateOp</param> /// <param name="relProperty">the relationship property</param> /// <returns>the navigateOp</returns> internal NavigateOp CreateNavigateOp(TypeUsage type, RelProperty relProperty) { // keep track of rel-properties AddRelPropertyReference(relProperty); return new NavigateOp(type, relProperty); }
/// <summary> /// Creates a nested property ref for a rel-property. Delegates to the function above /// </summary> /// <param name="p">the rel-property</param> /// <returns>a nested property ref</returns> internal PropertyRef CreateNestedPropertyRef(RelProperty p) { return CreateNestedPropertyRef(new RelPropertyRef(p)); }
private Node RewriteManyToManyNavigationProperty( RelProperty relProperty, List<RelationshipSet> relationshipSets, Node sourceRefNode) { PlanCompiler.Assert(relationshipSets.Count > 0, "expected at least one relationship set here"); PlanCompiler.Assert( relProperty.ToEnd.RelationshipMultiplicity == RelationshipMultiplicity.Many && relProperty.FromEnd.RelationshipMultiplicity == RelationshipMultiplicity.Many, "Expected target end multiplicity to be 'many'. Found " + relProperty + "; multiplicity = " + relProperty.ToEnd.RelationshipMultiplicity); Node ret = null; var joinNodes = new List<Node>(relationshipSets.Count); var outputVars = new List<Var>(relationshipSets.Count * 2); foreach (var r in relationshipSets) { Var rsVar; Var esVar; var joinNode = BuildJoinForNavProperty(r, relProperty.ToEnd, out rsVar, out esVar); joinNodes.Add(joinNode); outputVars.Add(rsVar); outputVars.Add(esVar); } // // Build the union-all node // Node unionAllNode; IList<Var> unionAllVars; m_command.BuildUnionAllLadder(joinNodes, outputVars, out unionAllNode, out unionAllVars); // // Now build out the filterOp over the left-side var // var rsSourceRefNode = m_command.CreateNode( m_command.CreatePropertyOp(relProperty.FromEnd), m_command.CreateNode(m_command.CreateVarRefOp(unionAllVars[0]))); var predicate = m_command.BuildComparison( OpType.EQ, sourceRefNode, rsSourceRefNode); var filterNode = m_command.CreateNode( m_command.CreateFilterOp(), unionAllNode, predicate); // // Finally, build out a project node that only projects out the entity side // var projectNode = m_command.BuildProject(filterNode, new[] { unionAllVars[1] }, new Node[] { }); // // Build a collectOp over the project node // ret = m_command.BuildCollect(projectNode, unionAllVars[1]); return ret; }
private Node RewriteNavigationProperty( NavigationProperty navProperty, Node sourceEntityNode, TypeUsage resultType) { var relProperty = new RelProperty(navProperty.RelationshipType, navProperty.FromEndMember, navProperty.ToEndMember); PlanCompiler.Assert( m_command.IsRelPropertyReferenced(relProperty) || (relProperty.ToEnd.RelationshipMultiplicity == RelationshipMultiplicity.Many), "Unreferenced rel property? " + relProperty); // Handle N:1 if ((relProperty.FromEnd.RelationshipMultiplicity == RelationshipMultiplicity.Many) && (relProperty.ToEnd.RelationshipMultiplicity != RelationshipMultiplicity.Many)) { return RewriteManyToOneNavigationProperty(relProperty, sourceEntityNode, resultType); } // // Find the list of all relationships that could satisfy this relationship // If we find no matching relationship set, simply return a null node / empty collection // var relationshipSets = GetRelationshipSets(relProperty.Relationship); if (relationshipSets.Count == 0) { // return an empty set / null node if (relProperty.ToEnd.RelationshipMultiplicity == RelationshipMultiplicity.Many) { return m_command.CreateNode(m_command.CreateNewMultisetOp(resultType)); } return m_command.CreateNode(m_command.CreateNullOp(resultType)); } // Build out a ref over the source entity var sourceRefNode = m_command.CreateNode( m_command.CreateGetEntityRefOp(relProperty.FromEnd.TypeUsage), sourceEntityNode); // Hanlde the 1:M and N:M cases if (relProperty.ToEnd.RelationshipMultiplicity == RelationshipMultiplicity.Many) { // Handle N:M if (relProperty.FromEnd.RelationshipMultiplicity == RelationshipMultiplicity.Many) { return RewriteManyToManyNavigationProperty(relProperty, relationshipSets, sourceRefNode); } // Handle 1:M return RewriteOneToManyNavigationProperty(relProperty, relationshipSets, sourceRefNode); } // Handle 1:1 return RewriteOneToOneNavigationProperty(relProperty, relationshipSets, sourceRefNode); }
/// <summary> /// Create a "relationship" propertyOp /// </summary> /// <param name="prop">the relationship property</param> /// <returns>a RelPropertyOp</returns> internal RelPropertyOp CreateRelPropertyOp(RelProperty prop) { AddRelPropertyReference(prop); return new RelPropertyOp(prop.ToEnd.TypeUsage, prop); }
/// <summary> /// Find the relationshipset that matches the current entityset + from/to roles /// </summary> /// <param name="entitySet"></param> /// <param name="relProperty"></param> /// <returns></returns> private static RelationshipSet FindRelationshipSet(EntitySetBase entitySet, RelProperty relProperty) { foreach (var es in entitySet.EntityContainer.BaseEntitySets) { var rs = es as AssociationSet; if (rs != null && rs.ElementType.EdmEquals(relProperty.Relationship) && rs.AssociationSetEnds[relProperty.FromEnd.Identity].EntitySet.EdmEquals(entitySet)) { return rs; } } return null; }
/// <summary> /// Simple constructor /// </summary> /// <param name="property">the property metadata</param> internal RelPropertyRef(RelProperty property) { m_property = property; }