private static bool UpdateColumnNamesForTableSharing( DbDatabaseMapping databaseMapping, EdmEntityType entityType, DbTableMetadata toTable, DbEntityTypeMappingFragment fragment) { // Validate: this table can be used only if: // 1. The table is not used by any other type // 2. The table is used only by types in the same type hierarchy (TPH) // 3. There is a 1:1 relationship and the PK count and types match (Table Splitting) var typesSharingTable = FindAllTypesUsingTable(databaseMapping, toTable); var associationsToSharedTable = new Dictionary<EdmEntityType, List<EdmAssociationType>>(); foreach (var candidateType in typesSharingTable) { var oneToOneAssocations = FindAllOneToOneFKAssociationTypes( databaseMapping.Model, entityType, candidateType); var rootType = candidateType.GetRootType(); if (!associationsToSharedTable.ContainsKey(rootType)) { associationsToSharedTable.Add(rootType, oneToOneAssocations.ToList()); } else { associationsToSharedTable[rootType].AddRange(oneToOneAssocations); } } foreach (var candidateTypePair in associationsToSharedTable) { // Check if these types are in a TPH hierarchy if (candidateTypePair.Key != entityType.GetRootType() && candidateTypePair.Value.Count == 0) { var tableName = toTable.GetTableName(); throw Error.EntityMappingConfiguration_InvalidTableSharing( entityType.Name, candidateTypePair.Key.Name, tableName != null ? tableName.Name : toTable.DatabaseIdentifier); } } var allAssociations = associationsToSharedTable.Values.SelectMany(l => l); if (allAssociations.Any()) { var principalKeyNamesType = toTable.GetKeyNamesType(); if (principalKeyNamesType == null) { // grab a candidate var association = allAssociations.First(); principalKeyNamesType = association.Constraint.PrincipalEnd(association).EntityType; if (allAssociations.All(x => x.Constraint.PrincipalEnd(x).EntityType == principalKeyNamesType)) { toTable.SetKeyNamesType(principalKeyNamesType); } } // rename the columns in the fragment to match the principal keys var principalKeys = principalKeyNamesType.KeyProperties().ToArray(); var i = 0; foreach (var k in entityType.KeyProperties()) { var dependentColumn = fragment.PropertyMappings.Single(pm => pm.PropertyPath.First() == k).Column; dependentColumn.Name = principalKeys[i].Name; i++; } return true; } return false; }
private HashSet<EdmPropertyPath> DiscoverAllMappingsToContain( DbDatabaseMapping databaseMapping, EdmEntityType entityType, DbTableMetadata toTable, bool isSharingTableWithBase) { // Ensure all specified properties are the only ones present in this fragment and table var mappingsToContain = new HashSet<EdmPropertyPath>(); // Include Key Properties always entityType.KeyProperties().Each( p => mappingsToContain.AddRange(p.ToPropertyPathList())); // Include All Inherited Properties if (MapInheritedProperties) { entityType.Properties.Except(entityType.DeclaredProperties).Each( p => mappingsToContain.AddRange(p.ToPropertyPathList())); } // If sharing table with base type, include all the mappings that the base has if (isSharingTableWithBase) { var baseMappingsToContain = new HashSet<EdmPropertyPath>(); var baseType = entityType.BaseType; DbEntityTypeMapping baseMapping = null; DbEntityTypeMappingFragment baseFragment = null; // if the base is abstract it may have no mapping so look upwards until you find either: // 1. a type with mappings and // 2. if none can be found (abstract until the root or hit another table), then include all declared properties on that base type while (baseType != null && baseMapping == null) { baseMapping = databaseMapping.GetEntityTypeMapping(entityType.BaseType); if (baseMapping != null) { baseFragment = baseMapping.TypeMappingFragments.SingleOrDefault(tmf => tmf.Table == toTable); } if (baseFragment == null) { baseType.DeclaredProperties.Each( p => baseMappingsToContain.AddRange(p.ToPropertyPathList())); } baseType = baseType.BaseType; } if (baseFragment != null) { foreach (var pm in baseFragment.PropertyMappings) { mappingsToContain.Add(new EdmPropertyPath(pm.PropertyPath)); } } mappingsToContain.AddRange(baseMappingsToContain); } if (Properties == null) { // Include All Declared Properties entityType.DeclaredProperties.Each( p => mappingsToContain.AddRange(p.ToPropertyPathList())); } else { // Include Specific Properties Properties.Each( p => mappingsToContain.AddRange(PropertyPathToEdmPropertyPath(p, entityType))); } return mappingsToContain; }
private DbEntityTypeMappingFragment FindOrCreateTypeMappingFragment( DbDatabaseMapping databaseMapping, ref DbEntityTypeMapping entityTypeMapping, int configurationIndex, EdmEntityType entityType, DbProviderManifest providerManifest) { DbEntityTypeMappingFragment fragment = null; if (entityTypeMapping == null) { Contract.Assert(entityType.IsAbstract); new EntityTypeMappingGenerator(providerManifest). Generate(entityType, databaseMapping); entityTypeMapping = databaseMapping.GetEntityTypeMapping(entityType); configurationIndex = 0; } if (configurationIndex < entityTypeMapping.TypeMappingFragments.Count) { fragment = entityTypeMapping.TypeMappingFragments[configurationIndex]; } else { if (MapInheritedProperties) { throw Error.EntityMappingConfiguration_DuplicateMapInheritedProperties(entityType.Name); } else if (Properties == null) { throw Error.EntityMappingConfiguration_DuplicateMappedProperties(entityType.Name); } else { Properties.Each( p => { if ( PropertyPathToEdmPropertyPath(p, entityType).Any( pp => !entityType.KeyProperties().Contains(pp.First()))) { throw Error.EntityMappingConfiguration_DuplicateMappedProperty( entityType.Name, p.ToString()); } }); } // Special case where they've asked for an extra table related to this type that only will include the PK columns // Uniquify: can be false, always move to a new table var templateTable = entityTypeMapping.TypeMappingFragments[0].Table; fragment = EntityMappingOperations.CreateTypeMappingFragment( entityTypeMapping, entityTypeMapping.TypeMappingFragments[0], databaseMapping.Database.AddTable(templateTable.Name, templateTable)); } return fragment; }
public bool MapsAnyInheritedProperties(EdmEntityType entityType) { var properties = new HashSet<EdmPropertyPath>(); if (Properties != null) { Properties.Each( p => properties.AddRange(PropertyPathToEdmPropertyPath(p, entityType))); } return MapInheritedProperties || properties.Any( x => !entityType.KeyProperties().Contains(x.First()) && !entityType.DeclaredProperties.Contains(x.First())); }
public void Generate( EdmEntityType entityType, IEnumerable<EdmProperty> properties, DbEntitySetMapping entitySetMapping, DbEntityTypeMappingFragment entityTypeMappingFragment, IList<EdmProperty> propertyPath, bool createNewColumn) { Contract.Requires(entityType != null); Contract.Requires(properties != null); Contract.Requires(entityTypeMappingFragment != null); Contract.Requires(propertyPath != null); var rootDeclaredProperties = entityType.GetRootType().DeclaredProperties; foreach (var property in properties) { if (property.PropertyType.IsComplexType && propertyPath.Any( p => p.PropertyType.IsComplexType && (p.PropertyType.ComplexType == property.PropertyType.ComplexType))) { throw Error.CircularComplexTypeHierarchy(); } propertyPath.Add(property); if (property.PropertyType.IsComplexType) { Generate( entityType, property.PropertyType.ComplexType.DeclaredProperties, entitySetMapping, entityTypeMappingFragment, propertyPath, createNewColumn); } else { var tableColumn = entitySetMapping.EntityTypeMappings .SelectMany(etm => etm.TypeMappingFragments) .SelectMany(etmf => etmf.PropertyMappings) .Where(pm => pm.PropertyPath.SequenceEqual(propertyPath)) .Select(pm => pm.Column) .FirstOrDefault(); if (tableColumn == null || createNewColumn) { tableColumn = entityTypeMappingFragment.Table.AddColumn( string.Join("_", propertyPath.Select(p => p.Name))); MapTableColumn( property, tableColumn, !rootDeclaredProperties.Contains(propertyPath.First()), entityType.KeyProperties().Contains(property)); } entityTypeMappingFragment.PropertyMappings.Add( new DbEdmPropertyMapping { Column = tableColumn, PropertyPath = propertyPath.ToList() }); } propertyPath.Remove(property); } }
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 }); } } }