/// <summary> /// Merge the discriminatorMap info we just found with what we've already found. /// In practice, if either the current or the new map is from an OfTypeOnly view, we /// have to avoid the optimizations. /// If we have a new map that is a superset of the current map, then we can just swap /// the new map for the current one. /// If the current map is tha super set of the new one ther's nothing to do. /// (Of course, if neither has changed, then we really don't need to look) /// </summary> internal void Merge(EntityTypeBase neededRootEntityType, bool includesSubtypes, ExplicitDiscriminatorMap discriminatorMap) { // If what we've found doesn't exactly match what we are looking for we have more work to do if (RootEntityType != neededRootEntityType || IncludesSubTypes != includesSubtypes) { if (!IncludesSubTypes || !includesSubtypes) { // If either the original or the new map is from an of-type-only view we can't // merge, we just have to not optimize this case. DiscriminatorMap = null; } if (TypeSemantics.IsSubTypeOf(RootEntityType, neededRootEntityType)) { // we're asking for a super type of existing type, and what we had is a proper // subset of it -we can replace the existing item. RootEntityType = neededRootEntityType; DiscriminatorMap = discriminatorMap; } if (!TypeSemantics.IsSubTypeOf(neededRootEntityType, RootEntityType)) { // If either the original or the new map is from an of-type-only view we can't // merge, we just have to not optimize this case. DiscriminatorMap = null; } } }
private void CheckClearedEntryOnSpan( object targetValue, IEntityWrapper wrappedSource, EntityKey sourceKey, AssociationEndMember targetMember) { // If a relationship does not exist on the server but does exist on the client, // we may need to remove it, depending on the current state and the MergeOption if ((null != (object)sourceKey) && (null == targetValue) && (MergeOption == MergeOption.PreserveChanges || MergeOption == MergeOption.OverwriteChanges)) { // When the spanned value is null, it may be because the spanned association applies to a // subtype of the entity's type, and the entity is not actually an instance of that type. var sourceEnd = MetadataHelper.GetOtherAssociationEnd(targetMember); EdmType expectedSourceType = ((RefType)sourceEnd.TypeUsage.EdmType).ElementType; TypeUsage entityTypeUsage; if (!Context.Perspective.TryGetType(wrappedSource.IdentityType, out entityTypeUsage) || entityTypeUsage.EdmType.EdmEquals(expectedSourceType) || TypeSemantics.IsSubTypeOf(entityTypeUsage.EdmType, expectedSourceType)) { // Otherwise, the source entity is the correct type (exactly or a subtype) for the source // end of the spanned association, so validate that the relationhip that was spanned is // part of the Container owning the EntitySet of the root entity. // This can be done by comparing the EntitySet of the row's entity to the relationships // in the Container and their AssociationSetEnd's type CheckClearedEntryOnSpan(sourceKey, targetMember); } } }
internal DbRelatedEntityRef( RelationshipEndMember sourceEnd, RelationshipEndMember targetEnd, DbExpression targetEntityRef) { if (!object.ReferenceEquals((object)sourceEnd.DeclaringType, (object)targetEnd.DeclaringType)) { throw new ArgumentException(Strings.Cqt_RelatedEntityRef_TargetEndFromDifferentRelationship, nameof(targetEnd)); } if (object.ReferenceEquals((object)sourceEnd, (object)targetEnd)) { throw new ArgumentException(Strings.Cqt_RelatedEntityRef_TargetEndSameAsSourceEnd, nameof(targetEnd)); } if (targetEnd.RelationshipMultiplicity != RelationshipMultiplicity.One && targetEnd.RelationshipMultiplicity != RelationshipMultiplicity.ZeroOrOne) { throw new ArgumentException(Strings.Cqt_RelatedEntityRef_TargetEndMustBeAtMostOne, nameof(targetEnd)); } if (!TypeSemantics.IsReferenceType(targetEntityRef.ResultType)) { throw new ArgumentException(Strings.Cqt_RelatedEntityRef_TargetEntityNotRef, nameof(targetEntityRef)); } EntityTypeBase elementType1 = TypeHelpers.GetEdmType <RefType>(targetEnd.TypeUsage).ElementType; EntityTypeBase elementType2 = TypeHelpers.GetEdmType <RefType>(targetEntityRef.ResultType).ElementType; if (!elementType1.EdmEquals((MetadataItem)elementType2) && !TypeSemantics.IsSubTypeOf((EdmType)elementType2, (EdmType)elementType1)) { throw new ArgumentException(Strings.Cqt_RelatedEntityRef_TargetEntityNotCompatible, nameof(targetEntityRef)); } this._targetEntityRef = targetEntityRef; this._targetEnd = targetEnd; this._sourceEnd = sourceEnd; }
internal DbRelatedEntityRef(RelationshipEndMember sourceEnd, RelationshipEndMember targetEnd, DbExpression targetEntityRef) { // Validate that the specified relationship ends are: // 1. Non-null // 2. From the same metadata workspace as that used by the command tree DebugCheck.NotNull(sourceEnd); DebugCheck.NotNull(targetEnd); // Validate that the specified target entity ref is: // 1. Non-null DebugCheck.NotNull(targetEntityRef); // Validate that the specified source and target ends are: // 1. Declared by the same relationship type if (!ReferenceEquals(sourceEnd.DeclaringType, targetEnd.DeclaringType)) { throw new ArgumentException(Strings.Cqt_RelatedEntityRef_TargetEndFromDifferentRelationship, "targetEnd"); } // 2. Not the same end if (ReferenceEquals(sourceEnd, targetEnd)) { throw new ArgumentException(Strings.Cqt_RelatedEntityRef_TargetEndSameAsSourceEnd, "targetEnd"); } // Validate that the specified target end has multiplicity of at most one if (targetEnd.RelationshipMultiplicity != RelationshipMultiplicity.One && targetEnd.RelationshipMultiplicity != RelationshipMultiplicity.ZeroOrOne) { throw new ArgumentException(Strings.Cqt_RelatedEntityRef_TargetEndMustBeAtMostOne, "targetEnd"); } // Validate that the specified target entity ref actually has a ref result type if (!TypeSemantics.IsReferenceType(targetEntityRef.ResultType)) { throw new ArgumentException(Strings.Cqt_RelatedEntityRef_TargetEntityNotRef, "targetEntityRef"); } // Validate that the specified target entity is of a type that can be reached by navigating to the specified relationship end var endType = TypeHelpers.GetEdmType <RefType>(targetEnd.TypeUsage).ElementType; var targetType = TypeHelpers.GetEdmType <RefType>(targetEntityRef.ResultType).ElementType; if (!endType.EdmEquals(targetType) && !TypeSemantics.IsSubTypeOf(targetType, endType)) { throw new ArgumentException(Strings.Cqt_RelatedEntityRef_TargetEntityNotCompatible, "targetEntityRef"); } // Validation succeeded, initialize state _targetEntityRef = targetEntityRef; _targetEnd = targetEnd; _sourceEnd = sourceEnd; }
// <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)) { var fromEntityType = (EntityType)((RefType)fromEnd.TypeUsage.EdmType).ElementType; return(EntityTypeEquals(compareType, fromEntityType) || TypeSemantics.IsSubTypeOf(compareType, fromEntityType) || TypeSemantics.IsSubTypeOf(fromEntityType, compareType)); } return(false); }
private static bool IsValidRelationshipSpan( EntityType compareType, AssociationType associationType, AssociationEndMember fromEnd, AssociationEndMember toEnd) { if (associationType.IsForeignKey || RelationshipMultiplicity.One != toEnd.RelationshipMultiplicity && toEnd.RelationshipMultiplicity != RelationshipMultiplicity.ZeroOrOne) { return(false); } EntityType elementType = (EntityType)((RefType)fromEnd.TypeUsage.EdmType).ElementType; if (!ObjectSpanRewriter.EntityTypeEquals((EntityTypeBase)compareType, (EntityTypeBase)elementType) && !TypeSemantics.IsSubTypeOf((EdmType)compareType, (EdmType)elementType)) { return(TypeSemantics.IsSubTypeOf((EdmType)elementType, (EdmType)compareType)); } return(true); }
internal void Merge( EntityTypeBase neededRootEntityType, bool includesSubtypes, ExplicitDiscriminatorMap discriminatorMap) { if (this.RootEntityType == neededRootEntityType && this.IncludesSubTypes == includesSubtypes) { return; } if (!this.IncludesSubTypes || !includesSubtypes) { this.DiscriminatorMap = (ExplicitDiscriminatorMap)null; } if (TypeSemantics.IsSubTypeOf((EdmType)this.RootEntityType, (EdmType)neededRootEntityType)) { this.RootEntityType = neededRootEntityType; this.DiscriminatorMap = discriminatorMap; } if (TypeSemantics.IsSubTypeOf((EdmType)neededRootEntityType, (EdmType)this.RootEntityType)) { return; } this.DiscriminatorMap = (ExplicitDiscriminatorMap)null; }
private void CheckClearedEntryOnSpan( object targetValue, IEntityWrapper wrappedSource, EntityKey sourceKey, AssociationEndMember targetMember) { if ((object)sourceKey == null || targetValue != null || this.MergeOption != MergeOption.PreserveChanges && this.MergeOption != MergeOption.OverwriteChanges) { return; } EdmType elementType = (EdmType)((RefType)MetadataHelper.GetOtherAssociationEnd(targetMember).TypeUsage.EdmType).ElementType; TypeUsage outTypeUsage; if (this.Context.Perspective.TryGetType(wrappedSource.IdentityType, out outTypeUsage) && !outTypeUsage.EdmType.EdmEquals((MetadataItem)elementType) && !TypeSemantics.IsSubTypeOf(outTypeUsage.EdmType, elementType)) { return; } this.CheckClearedEntryOnSpan(sourceKey, targetMember); }