public void Configure_should_split_key_constraint_when_to_table_configuration()
        {
            var database = new DbDatabaseMetadata().Initialize();
            var sourceTable = database.AddTable("Source");
            var fkColumn = sourceTable.AddColumn("Fk");
            var foreignKeyConstraint = new DbForeignKeyConstraintMetadata();
            foreignKeyConstraint.DependentColumns.Add(fkColumn);
            sourceTable.ForeignKeyConstraints.Add(foreignKeyConstraint);
            var targetTable = database.AddTable("Split");
            var associationSetMapping = new DbAssociationSetMapping().Initialize();
            associationSetMapping.Table = sourceTable;
            associationSetMapping.SourceEndMapping.PropertyMappings.Add(new DbEdmPropertyMapping { Column = fkColumn });

            var independentAssociationMappingConfiguration
                = new ForeignKeyAssociationMappingConfiguration();

            independentAssociationMappingConfiguration.ToTable("Split");

            independentAssociationMappingConfiguration.Configure(associationSetMapping, database);

            Assert.True(targetTable.Columns.Contains(fkColumn));
            Assert.True(targetTable.ForeignKeyConstraints.Contains(foreignKeyConstraint));
            Assert.False(sourceTable.Columns.Contains(fkColumn));
            Assert.False(sourceTable.ForeignKeyConstraints.Contains(foreignKeyConstraint));
            Assert.Same(targetTable, associationSetMapping.Table);
        }
        internal void WriteForeignKeyConstraintElement(
            DbTableMetadata dbTableMetadata, DbForeignKeyConstraintMetadata tableFKConstraint)
        {
            _xmlWriter.WriteStartElement(SsdlConstants.Element_Association);
            _xmlWriter.WriteAttributeString(SsdlConstants.Attribute_Name, tableFKConstraint.Name);

            var multiplicity = DetermineMultiplicity(dbTableMetadata, tableFKConstraint);

            // If the FK is a Self Ref, then we need to append a suffix to the second role name
            var roleNames = DbModelSsdlHelper.GetRoleNamePair(tableFKConstraint.PrincipalTable, dbTableMetadata);

            // End
            WriteAssociationEndElementHeader(roleNames[0], tableFKConstraint.PrincipalTable, multiplicity.Key);

            if (tableFKConstraint.DeleteAction
                != DbOperationAction.None)
            {
                WriteOperationActionElement(SsdlConstants.Element_OnDelete, tableFKConstraint.DeleteAction);
            }

            WriteEndElement();
            WriteAssociationEndElementHeader(roleNames[1], dbTableMetadata, multiplicity.Value);
            WriteEndElement();

            // ReferentialConstraint
            WriteReferentialConstraintElementHeader();
            WriteReferentialConstraintRoleElement(
                SsdlConstants.Element_PrincipalRole, roleNames[0], tableFKConstraint.PrincipalTable.KeyColumns);
            WriteReferentialConstraintRoleElement(
                SsdlConstants.Element_DependentRole, roleNames[1], tableFKConstraint.DependentColumns);
            WriteEndElement();

            WriteEndElement();
        }
        public void Apply_should_introduce_cascade_delete_on_constraints()
        {
            var databaseMapping
                = new DbDatabaseMapping()
                    .Initialize(new EdmModel().Initialize(), new DbDatabaseMetadata().Initialize());

            var foreignKeyConstraint = new DbForeignKeyConstraintMetadata();

            Assert.Equal(DbOperationAction.None, foreignKeyConstraint.DeleteAction);

            var table = new DbTableMetadata();
            table.ForeignKeyConstraints.Add(foreignKeyConstraint);

            var associationType = new EdmAssociationType().Initialize();
            associationType.SourceEnd.EndKind = EdmAssociationEndKind.Many;
            associationType.SourceEnd.EntityType = new EdmEntityType();
            associationType.TargetEnd.EndKind = EdmAssociationEndKind.Many;
            associationType.TargetEnd.EntityType = new EdmEntityType();

            var associationSetMapping = databaseMapping.AddAssociationSetMapping(new EdmAssociationSet { ElementType = associationType });
            associationSetMapping.Table = table;

            ((IDbMappingConvention)new ManyToManyCascadeDeleteConvention()).Apply(databaseMapping);

            Assert.Equal(DbOperationAction.Cascade, foreignKeyConstraint.DeleteAction);
        }
 internal void WriteAssociationSetElementHeader(DbForeignKeyConstraintMetadata constraint)
 {
     _xmlWriter.WriteStartElement(SsdlConstants.Element_AssociationSet);
     _xmlWriter.WriteAttributeString(SsdlConstants.Attribute_Name, constraint.Name);
     _xmlWriter.WriteAttributeString(
         SsdlConstants.Attribute_Association, GetQualifiedTypeName(SsdlConstants.Value_Self, constraint.Name));
 }
 public static bool ContainsEquivalentForeignKey(
     this DbTableMetadata table, DbForeignKeyConstraintMetadata foreignKey)
 {
     return table.ForeignKeyConstraints
         .Any(
             fk => fk.PrincipalTable == foreignKey.PrincipalTable
                   && fk.DependentColumns.SequenceEqual(foreignKey.DependentColumns));
 }
        private static void GenerateForeignKeyAssociationType(
            EdmAssociationType associationType, DbDatabaseMapping databaseMapping)
        {
            //Contract.Requires(associationType != null);
            //Contract.Requires(databaseMapping != null);
            Contract.Assert(associationType.Constraint != null);

            var dependentEnd = associationType.Constraint.DependentEnd;
            var principalEnd = associationType.GetOtherEnd(dependentEnd);
            var principalEntityTypeMapping = GetEntityTypeMappingInHierarchy(databaseMapping, principalEnd.EntityType);
            var dependentEntityTypeMapping = GetEntityTypeMappingInHierarchy(databaseMapping, dependentEnd.EntityType);

            var foreignKeyConstraint = new DbForeignKeyConstraintMetadata
                {
                    Name = associationType.Name,
                    PrincipalTable =
                        principalEntityTypeMapping.TypeMappingFragments.Single().Table,
                    DeleteAction = principalEnd.DeleteAction.HasValue
                                       ? (DbOperationAction)principalEnd.DeleteAction.Value
                                       : DbOperationAction.None
                };

            foreach (var dependentProperty in associationType.Constraint.DependentProperties)
            {
                foreignKeyConstraint.DependentColumns.Add(
                    dependentEntityTypeMapping.GetPropertyMapping(dependentProperty).Column);
            }

            foreignKeyConstraint.SetAssociationType(associationType);

            dependentEntityTypeMapping
                .TypeMappingFragments
                .Single()
                .Table
                .ForeignKeyConstraints.Add(foreignKeyConstraint);
        }
 protected virtual void VisitDbForeignKeyConstraintMetadata(DbForeignKeyConstraintMetadata item)
 {
     VisitDbConstraintMetadata(item);
     if (item != null)
     {
         if (item.HasDependentColumns)
         {
             VisitCollection(item.DependentColumns, VisitDbTableColumnMetadata);
         }
         VisitDbTableMetadata(item.PrincipalTable);
     }
 }
        public static void AddTypeConstraint(
            EdmEntityType entityType, DbTableMetadata principalTable, DbTableMetadata dependentTable, bool isSplitting)
        {
            Contract.Requires(principalTable != null);
            Contract.Requires(dependentTable != null);
            Contract.Requires(entityType != null);

            var foreignKeyConstraintMetadata = new DbForeignKeyConstraintMetadata
                {
                    Name =
                        String.Format(
                            CultureInfo.InvariantCulture,
                            "{0}_TypeConstraint_From_{1}_To_{2}",
                            entityType.Name,
                            principalTable.Name,
                            dependentTable.Name),
                    PrincipalTable = principalTable
                };

            if (isSplitting)
            {
                foreignKeyConstraintMetadata.SetIsSplitConstraint();
            }
            else
            {
                foreignKeyConstraintMetadata.SetIsTypeConstraint();
            }
            dependentTable.Columns
                .Where(c => c.IsPrimaryKeyColumn)
                .Each(c => foreignKeyConstraintMetadata.DependentColumns.Add(c));

            dependentTable.ForeignKeyConstraints.Add(foreignKeyConstraintMetadata);

            //If "DbStoreGeneratedPattern.Identity" was copied from the parent table, it should be removed
            dependentTable.Columns.Where(c => c.IsPrimaryKeyColumn).Each(c => c.RemoveStoreGeneratedIdentityPattern());
        }
 private static void SetAllDependentColumns(
     DbForeignKeyConstraintMetadata fk,
     IEnumerable<DbTableColumnMetadata> sourceColumns,
     IEnumerable<DbTableColumnMetadata> destinationColumns)
 {
     foreach (var dc in sourceColumns)
     {
         fk.DependentColumns.Add(
             destinationColumns.Single(
                 c =>
                 string.Equals(c.Name, dc.Name, StringComparison.Ordinal)
                 || string.Equals(c.GetUnpreferredUniqueName(), dc.Name, StringComparison.Ordinal)));
     }
 }
        private static void CopyForeignKeyConstraint(
            DbDatabaseMetadata database, DbTableMetadata toTable,
            DbForeignKeyConstraintMetadata fk)
        {
            Contract.Requires(toTable != null);
            Contract.Requires(fk != null);

            var newFk = new DbForeignKeyConstraintMetadata
                {
                    DeleteAction = fk.DeleteAction,
                    Name =
                        database.Schemas.Single().Tables.SelectMany(t => t.ForeignKeyConstraints).
                            UniquifyName(fk.Name),
                    PrincipalTable = fk.PrincipalTable
                };

            // Make sure all the dependent columns refer to columns in the newTable
            SetAllDependentColumns(newFk, fk.DependentColumns, toTable.Columns);

            if (!toTable.ContainsEquivalentForeignKey(newFk))
            {
                toTable.ForeignKeyConstraints.Add(newFk);
            }
        }
        /// <summary>
        ///     Moves a foreign key constraint from oldTable to newTable and updates column references
        /// </summary>
        private static void MoveForeignKeyConstraint(
            DbTableMetadata fromTable, DbTableMetadata toTable, DbForeignKeyConstraintMetadata fk)
        {
            Contract.Requires(fromTable != null);
            Contract.Requires(toTable != null);
            Contract.Requires(fk != null);

            fromTable.ForeignKeyConstraints.Remove(fk);

            // Only move it to the new table if the destination is not the principal table or if all dependent columns are not FKs
            // Otherwise you end up with an FK from the PKs to the PKs of the same table
            if (fk.PrincipalTable != toTable
                ||
                !fk.DependentColumns.All(c => c.IsPrimaryKeyColumn))
            {
                // Make sure all the dependent columns refer to columns in the newTable
                var oldColumns = fk.DependentColumns.ToArray();
                fk.DependentColumns.Clear();
                SetAllDependentColumns(fk, oldColumns, toTable.Columns);

                if (!toTable.ContainsEquivalentForeignKey(fk))
                {
                    toTable.ForeignKeyConstraints.Add(fk);
                }
            }
        }
        private static KeyValuePair<string, string> DetermineMultiplicity(
            DbTableMetadata dependentTable, DbForeignKeyConstraintMetadata constraint)
        {
            var principalMultiplicity = CsdlConstants.Value_EndOptional;
            var dependentMultiplicity = CsdlConstants.Value_EndMany;
            var isDependentPropertiesFullyCoverKey = false;
            var isDependentPropertiesHasNullableProperty = false;

            IEnumerable<DbTableColumnMetadata> dependentProperties = constraint.DependentColumns;

            if (dependentTable.KeyColumns.Count() == dependentProperties.Count()
                && dependentTable.KeyColumns.All(k => dependentProperties.Contains(k)))
            {
                isDependentPropertiesFullyCoverKey = true;
            }

            if (dependentProperties.Any(p => p.IsNullable))
            {
                isDependentPropertiesHasNullableProperty = true;
            }

            if (!isDependentPropertiesHasNullableProperty)
            {
                principalMultiplicity = CsdlConstants.Value_EndRequired;
            }

            if (isDependentPropertiesFullyCoverKey)
            {
                principalMultiplicity = CsdlConstants.Value_EndRequired;
                dependentMultiplicity = CsdlConstants.Value_EndOptional;
            }

            return new KeyValuePair<string, string>(principalMultiplicity, dependentMultiplicity);
        }
        private void GenerateIndependentForeignKeyColumns(
            EdmEntityType principalEntityType,
            EdmEntityType dependentEntityType,
            DbAssociationSetMapping associationSetMapping,
            DbAssociationEndMapping associationEndMapping,
            DbTableMetadata dependentTable,
            DbForeignKeyConstraintMetadata foreignKeyConstraint,
            bool isPrimaryKeyColumn,
            EdmNavigationProperty principalNavigationProperty)
        {
            //Contract.Requires(principalEntityType != null);
            //Contract.Requires(associationEndMapping != null);
            //Contract.Requires(dependentTable != null);
            //Contract.Requires(foreignKeyConstraint != null);

            foreach (var property in principalEntityType.KeyProperties())
            {
                var foreignKeyColumn
                    = dependentTable.AddColumn(
                        ((principalNavigationProperty != null)
                             ? principalNavigationProperty.Name
                             : principalEntityType.Name)
                        + "_" + property.Name);

                MapTableColumn(property, foreignKeyColumn, false, isPrimaryKeyColumn);

                foreignKeyColumn.IsNullable = (associationEndMapping.AssociationEnd.IsOptional()
                                               ||
                                               (associationEndMapping.AssociationEnd.IsRequired()
                                                && dependentEntityType.BaseType != null));
                foreignKeyColumn.StoreGeneratedPattern = DbStoreGeneratedPattern.None;

                foreignKeyConstraint.DependentColumns.Add(foreignKeyColumn);

                associationEndMapping.PropertyMappings.Add(
                    new DbEdmPropertyMapping
                        {
                            Column = foreignKeyColumn,
                            PropertyPath = new[] { property }
                        });

                if (foreignKeyColumn.IsNullable)
                {
                    associationSetMapping.ColumnConditions.Add(
                        new DbColumnCondition
                            {
                                Column = foreignKeyColumn,
                                IsNull = false
                            });
                }
            }
        }
        private void GenerateIndependentForeignKeyConstraint(
            DbDatabaseMapping databaseMapping,
            EdmEntityType principalEntityType,
            EdmEntityType dependentEntityType,
            DbTableMetadata dependentTable,
            DbAssociationSetMapping associationSetMapping,
            DbAssociationEndMapping associationEndMapping,
            string name,
            EdmAssociationEnd principalEnd,
            bool isPrimaryKeyColumn = false)
        {
            //Contract.Requires(databaseMapping != null);
            //Contract.Requires(principalEntityType != null);
            //Contract.Requires(dependentTable != null);
            //Contract.Requires(associationEndMapping != null);
            //Contract.Requires(!string.IsNullOrWhiteSpace(name));

            var principalTable
                = GetEntityTypeMappingInHierarchy(databaseMapping, principalEntityType)
                    .TypeMappingFragments
                    .Single()
                    .Table;

            var foreignKeyConstraint = new DbForeignKeyConstraintMetadata
                {
                    Name = name,
                    PrincipalTable = principalTable,
                    DeleteAction = associationEndMapping.AssociationEnd.DeleteAction.HasValue
                                       ? (DbOperationAction)
                                         associationEndMapping.AssociationEnd.DeleteAction.
                                             Value
                                       : DbOperationAction.None
                };

            var principalNavigationProperty
                = databaseMapping.Model.GetEntityTypes()
                    .SelectMany(e => e.DeclaredNavigationProperties)
                    .SingleOrDefault(n => n.ResultEnd == principalEnd);

            GenerateIndependentForeignKeyColumns(
                principalEntityType,
                dependentEntityType,
                associationSetMapping,
                associationEndMapping,
                dependentTable,
                foreignKeyConstraint,
                isPrimaryKeyColumn,
                principalNavigationProperty);

            dependentTable.ForeignKeyConstraints.Add(foreignKeyConstraint);
        }