Example #1
0
        internal ForeignKeyConstraint(md.RelationshipType relType, md.RelationshipSet relationshipSet, md.ReferentialConstraint constraint)
        {
            md.AssociationSet       assocSet = relationshipSet as md.AssociationSet;
            md.AssociationEndMember fromEnd  = constraint.FromRole as md.AssociationEndMember;
            md.AssociationEndMember toEnd    = constraint.ToRole as md.AssociationEndMember;

            // Currently only Associations are supported
            if (null == assocSet || null == fromEnd || null == toEnd)
            {
                throw EntityUtil.NotSupported();
            }

            m_constraint = constraint;
            md.EntitySet parent = System.Data.Common.Utils.MetadataHelper.GetEntitySetAtEnd(assocSet, fromEnd); // relationshipSet.GetRelationshipEndExtent(constraint.FromRole);
            md.EntitySet child  = System.Data.Common.Utils.MetadataHelper.GetEntitySetAtEnd(assocSet, toEnd);   // relationshipSet.GetRelationshipEndExtent(constraint.ToRole);
            m_extentPair = new ExtentPair(parent, child);
            m_childKeys  = new List <string>();
            foreach (md.EdmProperty prop in constraint.ToProperties)
            {
                m_childKeys.Add(prop.Name);
            }

            m_parentKeys = new List <string>();
            foreach (md.EdmProperty prop in constraint.FromProperties)
            {
                m_parentKeys.Add(prop.Name);
            }

            PlanCompiler.Assert((md.RelationshipMultiplicity.ZeroOrOne == fromEnd.RelationshipMultiplicity || md.RelationshipMultiplicity.One == fromEnd.RelationshipMultiplicity), "from-end of relationship constraint cannot have multiplicity greater than 1");
        }
        private void InitializeForeignKeyLists()
        {
            var  dependents                   = new List <Tuple <AssociationSet, ReferentialConstraint> >();
            var  principals                   = new List <Tuple <AssociationSet, ReferentialConstraint> >();
            bool foundFkRelationship          = false;
            bool foundIndependentRelationship = false;

            foreach (AssociationSet associationSet in MetadataHelper.GetAssociationsForEntitySet(this))
            {
                if (associationSet.ElementType.IsForeignKey)
                {
                    foundFkRelationship = true;
                    Debug.Assert(associationSet.ElementType.ReferentialConstraints.Count == 1, "Expected exactly one constraint for FK");
                    ReferentialConstraint constraint = associationSet.ElementType.ReferentialConstraints[0];
                    if (constraint.ToRole.GetEntityType().IsAssignableFrom(this.ElementType) ||
                        this.ElementType.IsAssignableFrom(constraint.ToRole.GetEntityType()))
                    {
                        // Dependents
                        dependents.Add(new Tuple <AssociationSet, ReferentialConstraint>(associationSet, constraint));
                    }
                    if (constraint.FromRole.GetEntityType().IsAssignableFrom(this.ElementType) ||
                        this.ElementType.IsAssignableFrom(constraint.FromRole.GetEntityType()))
                    {
                        // Principals
                        principals.Add(new Tuple <AssociationSet, ReferentialConstraint>(associationSet, constraint));
                    }
                }
                else
                {
                    foundIndependentRelationship = true;
                }
            }

            _hasForeignKeyRelationships  = foundFkRelationship;
            _hasIndependentRelationships = foundIndependentRelationship;

            var readOnlyDependents = dependents.AsReadOnly();
            var readOnlyPrincipals = principals.AsReadOnly();

            Interlocked.CompareExchange(ref _foreignKeyDependents, readOnlyDependents, null);
            Interlocked.CompareExchange(ref _foreignKeyPrincipals, readOnlyPrincipals, null);
        }
 private bool IsEntityDependentSideOfBothAssociations(ReferentialConstraint constraint0, ReferentialConstraint constraint1)
 {
     return ((RefType)constraint0.ToRole.TypeUsage.EdmType).ElementType == _storeEntitySet.ElementType && ((RefType)constraint1.ToRole.TypeUsage.EdmType).ElementType == _storeEntitySet.ElementType;
 }
 private static void AddToPropertyNames(ReferentialConstraint constraint, Set<string> names)
 {
     foreach (EdmProperty property in constraint.ToProperties)
     {
         names.Add(property.Name);
     }
 }
 private bool AreAllEntityColumnsMappedAsToColumns(ReferentialConstraint constraint0, ReferentialConstraint constraint1)
 {
     Set<string> names = new Set<string>();
     AddToPropertyNames(constraint0, names);
     AddToPropertyNames(constraint1, names);
     return names.Count == _storeEntitySet.ElementType.Properties.Count;
 }
        internal override bool CheckReferentialConstraintDependentProperty(EntityEntry ownerEntry, ReferentialConstraint constraint)
        {
            // if the related end contains a real entity or is a reference with a detached entitykey, we need to check for RI constraints
            if (!IsEmpty())
            {
                return base.CheckReferentialConstraintDependentProperty(ownerEntry, constraint);
            }
            else if ((ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne ||
                      ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One)
                     &&
                     DetachedEntityKey != null)
            {
                // related end is empty, so we must have a reference with a detached key
                var detachedKey = DetachedEntityKey;
#if DEBUG
    // If the constraint is not PK<->PK then we can't validate it here.
    // This debug code checks that we don't try to validate it.
                var keyNames = new List<string>(
                    from v in detachedKey.EntityKeyValues
                    select v.Key);
                foreach (var prop in constraint.ToProperties)
                {
                    Debug.Assert(
                        keyNames.Contains(prop.Name),
                        "Attempt to validate constraint where some FK values are not in the dependent PK");
                }
#endif
                // don't need to validate the principal/detached key here because that has already been done during AttachContext
                if (!VerifyRIConstraintsWithRelatedEntry(constraint, detachedKey.FindValueByName, ownerEntry.EntityKey))
                {
                    return false;
                }
            }

            return true;
        }
        private ReferentialConstraint CreateReferentialConstraint(LoadMethodSessionState session, AssociationType storeAssociation)
        {
            Debug.Assert(session != null, "session parameter is null");
            Debug.Assert(storeAssociation != null, "storeAssociation parameter is null");
            Debug.Assert(storeAssociation.ReferentialConstraints.Count <= 1, "We don't have a reason to have more than one constraint yet");

            // does the store have any constraints
            if (storeAssociation.ReferentialConstraints.Count == 0)
            {    
                return null;
            }

            ReferentialConstraint storeConstraint = storeAssociation.ReferentialConstraints[0];
            Debug.Assert(storeConstraint.FromProperties.Count == storeConstraint.ToProperties.Count, "FromProperties and ToProperties have different counts");
            Debug.Assert(storeConstraint.FromProperties.Count != 0, "No properties in the constraint, why does the constraint exist?");
            Debug.Assert(storeConstraint.ToProperties[0].DeclaringType.BuiltInTypeKind == BuiltInTypeKind.EntityType, "The property is not from an EntityType");

            EntityType toType = (EntityType)storeConstraint.ToProperties[0].DeclaringType;
            // If we are generating foreign keys, there is always a referential constraint. Otherwise, check
            // if the dependent end includes key properties. If so, this implies that there is a referential
            // constraint. Otherwise, it is assumed that the foreign key properties are not defined in the 
            // entity (verified ealier).
            if (!this.GenerateForeignKeyProperties && !RequiresModelReferentialConstraint(storeConstraint, toType)) 
            {
                return null;
            }
            // we need a constraint so lets build it
            int count = storeConstraint.FromProperties.Count;
            EdmProperty[] fromProperties = new EdmProperty[count];
            EdmProperty[] toProperties = new EdmProperty[count];
            AssociationEndMember fromRole = session.MappingLookups.StoreAssociationEndMemberToModelAssociationEndMember[(AssociationEndMember)storeConstraint.FromRole];
            AssociationEndMember toRole = session.MappingLookups.StoreAssociationEndMemberToModelAssociationEndMember[(AssociationEndMember)storeConstraint.ToRole];
            for (int index = 0; index < count; index++)
            {
                fromProperties[index] = session.MappingLookups.StoreEdmPropertyToModelEdmProperty[storeConstraint.FromProperties[index]];
                toProperties[index] = session.MappingLookups.StoreEdmPropertyToModelEdmProperty[storeConstraint.ToProperties[index]];
            }

            ReferentialConstraint newConstraint = new ReferentialConstraint(
                fromRole,
                toRole,
                fromProperties,
                toProperties);

            return newConstraint;
        }
Example #8
0
 /// <summary>
 /// Add the given referential constraint to the collection of referential constraints
 /// </summary>
 /// <param name="referentialConstraint"></param>
 internal void AddReferentialConstraint(ReferentialConstraint referentialConstraint)
 {
     this.ReferentialConstraints.Source.Add(referentialConstraint);
 }
        protected virtual void Visit(ReferentialConstraint referentialConstraint)
        {
            foreach (var property in referentialConstraint.FromProperties)
            {
                Visit(property);
            }
            Visit(referentialConstraint.FromRole);

            foreach (var property in referentialConstraint.ToProperties)
            {
                Visit(property);
            }
            Visit(referentialConstraint.ToRole);
        }
        private bool CreateReferentialConstraint(LoadMethodSessionState session,
            AssociationType association,
            AssociationEndMember pkEnd,
            AssociationEndMember fkEnd,
            List<RelationshipDetailsRow> columns,
            List<EdmSchemaError> errors)
        {
            EdmProperty[] fromProperties = new EdmProperty[columns.Count];
            EdmProperty[] toProperties = new EdmProperty[columns.Count];
            EntityType pkEntityType = session.RelationshipEndTypeLookup[pkEnd];
            EntityType fkEntityType = session.RelationshipEndTypeLookup[fkEnd];
            for (int index = 0; index < columns.Count; index++)
            {
                EdmProperty property;

                if(!pkEntityType.Properties.TryGetValue(columns[index].PKColumn, false, out property))
                {
                    errors.Add(
                        new EdmSchemaError(
                          Strings.AssociationMissingKeyColumn(
                            pkEntityType.Name,
                            fkEntityType.Name,
                            pkEntityType.Name + "." + columns[index].PKColumn),
                          (int)ModelBuilderErrorCode.AssociationMissingKeyColumn,
                          EdmSchemaErrorSeverity.Warning));
                    return false;
                }
                fromProperties[index] = property;

                if(!fkEntityType.Properties.TryGetValue(columns[index].FKColumn, false, out property))
                {
                    errors.Add(
                        new EdmSchemaError(
                        Strings.AssociationMissingKeyColumn(
                            pkEntityType.Name,
                            fkEntityType.Name,
                            fkEntityType.Name + "." + columns[index].FKColumn),
                            (int)ModelBuilderErrorCode.AssociationMissingKeyColumn,
                        EdmSchemaErrorSeverity.Warning));
                    return false;
                }
                toProperties[index] = property;
            }

            ReferentialConstraint constraint = new ReferentialConstraint(pkEnd,
                fkEnd,
                fromProperties,
                toProperties);

            association.AddReferentialConstraint(constraint);
            return true;
        }
        /// <summary>
        /// Creates an EntityKey for a principal entity based on the foreign key values contained
        /// in this entity.  This implies that this entity is at the dependent end of the relationship.
        /// </summary>
        /// <param name="dependentEntry">The EntityEntry for the dependent that contains the FK</param>
        /// <param name="constraint">The constraint that describes this FK relationship</param>
        /// <param name="principalEntitySet">The entity set at the principal end of the the relationship</param>
        /// <param name="useOriginalValues">If true then the key will be constructed from the original FK values</param>
        /// <returns>The key, or null if any value in the key is null</returns>
        public static EntityKey CreateKeyFromForeignKeyValues(EntityEntry dependentEntry, ReferentialConstraint constraint, EntitySet principalEntitySet, bool useOriginalValues)
        {
            // Build the key values.  If any part of the key is null, then the entire key
            // is considered null.
            var dependentProps = constraint.ToProperties;
            int numValues = dependentProps.Count;
            if (numValues == 1)
            {
                object keyValue = useOriginalValues ?
                    dependentEntry.GetOriginalEntityValue(dependentProps.First().Name) :
                    dependentEntry.GetCurrentEntityValue(dependentProps.First().Name);
                return keyValue == DBNull.Value ? null : new EntityKey(principalEntitySet, keyValue);
            }

            // Note that the properties in the principal entity set may be in a different order than
            // they appear in the constraint.  Therefore, we create name value mappings to ensure that
            // the correct values are associated with the correct properties.
            // Unfortunately, there is not way to call the public EntityKey constructor that takes pairs
            // because the internal "object" constructor hides it.  Even this doesn't work:
            // new EntityKey(principalEntitySet, (IEnumerable<KeyValuePair<string, object>>)keyValues)
            string[] keyNames = principalEntitySet.ElementType.KeyMemberNames;
            Debug.Assert(keyNames.Length == numValues, "Number of entity set key names does not match constraint names");
            object[] values = new object[numValues];
            var principalProps = constraint.FromProperties;
            for (int i = 0; i < numValues; i++)
            {
                object value = useOriginalValues ?
                    dependentEntry.GetOriginalEntityValue(dependentProps[i].Name) :
                    dependentEntry.GetCurrentEntityValue(dependentProps[i].Name);
                if (value == DBNull.Value)
                {
                    return null;
                }
                int keyIndex = Array.IndexOf(keyNames, principalProps[i].Name);
                Debug.Assert(keyIndex >= 0 && keyIndex < numValues, "Could not find constraint prop name in entity set key names");
                values[keyIndex] = value;
            }
            return new EntityKey(principalEntitySet, values);
        }
 public virtual string WriteForeignKeyUpdateAction(ReferentialConstraint refConstraint)
 {
     return "ON UPDATE NO ACTION";
 }
 public virtual string WriteForeignKeyDeleteAction(ReferentialConstraint refConstraint)
 {
     return refConstraint.FromRole.DeleteBehavior == OperationAction.Cascade ? "ON DELETE CASCADE" : "ON DELETE NO ACTION";
 }
        public virtual string GetForeignKeyConstraintName(ReferentialConstraint constraint)
        {
            var name = constraint.FromRole.DeclaringType.Name;

            if (!name.StartsWith("FK_", StringComparison.InvariantCultureIgnoreCase))
            {
                name = "FK_" + name;
            }

            return name;
        }
 private void GetConstraints(out ReferentialConstraint constraint0, out ReferentialConstraint constraint1)
 {
     Debug.Assert(_storeAssociationSets.Count == 2, "don't call this method if you don't have two associations");
     Debug.Assert(_storeAssociationSets[0].ElementType.ReferentialConstraints.Count == 1, "no referenctial constraint for association[0]");
     Debug.Assert(_storeAssociationSets[1].ElementType.ReferentialConstraints.Count == 1, "no referenctial constraint for association[1]");
     constraint0 = _storeAssociationSets[0].ElementType.ReferentialConstraints[0];
     constraint1 = _storeAssociationSets[1].ElementType.ReferentialConstraints[0];
 }
        protected override void Visit(ReferentialConstraint referentialConstraint)
        {
            int index;
            if (!this.AddObjectToSeenListAndHashBuilder(referentialConstraint, out index))
            {
                return;
            }

            this.AddObjectStartDumpToHashBuilder(referentialConstraint, index);

            #region Inner data visit
            this.AddObjectContentToHashBuilder(referentialConstraint.Identity);

            base.Visit(referentialConstraint);

            #endregion

            this.AddObjectEndDumpToHashBuilder();
        }
 /// <summary>
 /// Add the given referential constraint to the collection of referential constraints
 /// </summary>
 /// <param name="referentialConstraint"></param>
 internal void AddReferentialConstraint(ReferentialConstraint referentialConstraint)
 {
     ReferentialConstraints.Source.Add(referentialConstraint);
 }
 private bool IsAtLeastOneColumnFKInBothAssociations(ReferentialConstraint constraint0, ReferentialConstraint constraint1)
 {
     return constraint1.ToProperties.Any(c => constraint0.ToProperties.Contains(c));
 }
 private static bool RequiresModelReferentialConstraint(ReferentialConstraint storeConstraint, EntityType toType)
 {
     return toType.KeyMembers.Contains(storeConstraint.ToProperties[0]);
 }
 private bool IsAtLeastOneColumnOfBothDependentRelationshipColumnSetsNonNullable(ReferentialConstraint constraint0, ReferentialConstraint constraint1)
 {
     return ToPropertyHasNonNullableColumn(constraint0) && ToPropertyHasNonNullableColumn(constraint1);
 }
        // Check if related entities contain proper property values 
        internal override bool CheckReferentialConstraintPrincipalProperty(EntityEntry ownerEntry, ReferentialConstraint constraint)
        {
            EntityKey principalKey;
            if (!IsEmpty())
            {
                var wrappedRelatedEntity = ReferenceValue;
                // For Added entities, it doesn't matter what the key value is since it can't be trusted anyway.
                if (wrappedRelatedEntity.ObjectStateEntry != null
                    && wrappedRelatedEntity.ObjectStateEntry.State == EntityState.Added)
                {
                    return true;
                }
                principalKey = ExtractPrincipalKey(wrappedRelatedEntity);
            }
            else if ((ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne ||
                      ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One)
                     &&
                     DetachedEntityKey != null)
            {
                // Generally for foreign keys we want to use the EntityKey to do RI constraint validation
                // However, if we are doing an Add/Attach, we should use the DetachedEntityKey because this is the value
                // set by the user while the entity was detached, and should be used until the entity is fully added/attached
                if (IsForeignKey &&
                    !(ObjectContext.ObjectStateManager.TransactionManager.IsAddTracking ||
                      ObjectContext.ObjectStateManager.TransactionManager.IsAttachTracking))
                {
                    principalKey = EntityKey;
                }
                else
                {
                    principalKey = DetachedEntityKey;
                }
            }
            else
            {
                // We only need to check for RI constraints if the related end contains a real entity or is a reference with a detached entitykey
                return true;
            }

            return VerifyRIConstraintsWithRelatedEntry(constraint, ownerEntry.GetCurrentEntityValue, principalKey);
        }
 private static bool ToPropertyHasNonNullableColumn(ReferentialConstraint constraint)
 {
     foreach (EdmProperty property in constraint.ToProperties)
     {
         if (!property.Nullable)
         {
             return true;
         }
     }
     return false;
 }
        private static DbRelatedEntityRef RelatedEntityRefFromAssociationSetEnd(EntityType constructedEntityType, DbNewInstanceExpression entityConstructor, AssociationSetEnd principalSetEnd, ReferentialConstraint fkConstraint)
        {
            EntityType principalEntityType = (EntityType)TypeHelpers.GetEdmType<RefType>(fkConstraint.FromRole.TypeUsage).ElementType;
            IList<DbExpression> principalKeyValues = null;
            
            // Create Entity Property/DbExpression value pairs from the entity constructor DbExpression,
            // then join these with the principal/dependent property pairs from the FK constraint
            // to produce principal property name/DbExpression value pairs from which to create the principal ref.
            //
            // Ideally the code would be as below, but anonymous types break asmmeta:
            //var keyPropAndValue =
            //    from pv in constructedEntityType.Properties.Select((p, idx) => new { DependentProperty = p, Value = entityConstructor.Arguments[idx] })
            //    join ft in fkConstraint.FromProperties.Select((fp, idx) => new { PrincipalProperty = fp, DependentProperty = fkConstraint.ToProperties[idx] })
            //    on pv.DependentProperty equals ft.DependentProperty
            //    select new { PrincipalProperty = ft.PrincipalProperty.Name, Value = pv.Value };
            //
            var keyPropAndValue =
                from pv in constructedEntityType.Properties.Select((p, idx) => Tuple.Create(p, entityConstructor.Arguments[idx])) // new { DependentProperty = p, Value = entityConstructor.Arguments[idx] })
                join ft in fkConstraint.FromProperties.Select((fp, idx) => Tuple.Create(fp, fkConstraint.ToProperties[idx])) //new { PrincipalProperty = fp, DependentProperty = fkConstraint.ToProperties[idx] })
                on pv.Item1 equals ft.Item2 //pv.DependentProperty equals ft.DependentProperty
                select Tuple.Create(ft.Item1.Name, pv.Item2); // new { PrincipalProperty = ft.PrincipalProperty.Name, Value = pv.Value };

            // If there is only a single property in the principal's key, then there is no ordering concern.
            // Otherwise, create a dictionary of principal key property name to DbExpression value so that
            // when used as the arguments to the ref expression, the dependent property values - used here
            // as principal key property values - are in the correct order, which is the same order as the
            // key members themselves.
            //
            if (fkConstraint.FromProperties.Count == 1)
            {
                var singleKeyNameAndValue = keyPropAndValue.Single();
                Debug.Assert(singleKeyNameAndValue.Item1 == fkConstraint.FromProperties[0].Name, "Unexpected single key property name");
                principalKeyValues = new[] { singleKeyNameAndValue.Item2 };
            }
            else
            {
                var keyValueMap = keyPropAndValue.ToDictionary(pav => pav.Item1, pav => pav.Item2, StringComparer.Ordinal);
                principalKeyValues = principalEntityType.KeyMemberNames.Select(memberName => keyValueMap[memberName]).ToList();
            }
                        
            // Create the ref to the principal entity based on the (now correctly ordered) key value expressions.
            //
            DbRefExpression principalRef = principalSetEnd.EntitySet.CreateRef(principalEntityType, principalKeyValues);
            DbRelatedEntityRef result = DbExpressionBuilder.CreateRelatedEntityRef(fkConstraint.ToRole, fkConstraint.FromRole, principalRef);

            return result;
        }
 public override string WriteForeignKeyUpdateAction(ReferentialConstraint refConstraint)
 {
     return string.Empty;
 }