Skip to content

Commit

Permalink
Add support for seed data for entity types using table sharing where …
Browse files Browse the repository at this point in the history
…the principal is using TPT

Sort table mappings consistently
Remove discriminator-related facets completely when using TPT
Simplify SharedTableEntryMap

Part of #2266
  • Loading branch information
AndriySvyryd committed May 30, 2020
1 parent 0afeb92 commit aee7634
Show file tree
Hide file tree
Showing 22 changed files with 264 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,7 @@ protected virtual void GeneratePropertyAnnotations([NotNull] IProperty property,
RelationalAnnotationNames.ColumnType,
RelationalAnnotationNames.TableColumnMappings,
RelationalAnnotationNames.ViewColumnMappings,
RelationalAnnotationNames.RelationalOverrides,
CoreAnnotationNames.ValueGeneratorFactory,
CoreAnnotationNames.PropertyAccessMode,
CoreAnnotationNames.ChangeTrackingStrategy,
Expand Down Expand Up @@ -849,7 +850,9 @@ protected virtual void GenerateEntityTypeAnnotations(
var discriminatorMappingCompleteAnnotation = annotations.FirstOrDefault(a => a.Name == CoreAnnotationNames.DiscriminatorMappingComplete);
var discriminatorValueAnnotation = annotations.FirstOrDefault(a => a.Name == CoreAnnotationNames.DiscriminatorValue);

if ((discriminatorPropertyAnnotation ?? discriminatorMappingCompleteAnnotation ?? discriminatorValueAnnotation) != null)
if ((discriminatorPropertyAnnotation?.Value
?? discriminatorMappingCompleteAnnotation?.Value
?? discriminatorValueAnnotation?.Value) != null)
{
stringBuilder
.AppendLine()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,8 @@ private IEnumerable<string> GetAnnotationNamespaces(IEnumerable<IAnnotatable> it
RelationalAnnotationNames.ViewColumnMappings,
RelationalAnnotationNames.ForeignKeyMappings,
RelationalAnnotationNames.TableIndexMappings,
RelationalAnnotationNames.UniqueConstraintMappings
RelationalAnnotationNames.UniqueConstraintMappings,
RelationalAnnotationNames.RelationalOverrides
};

return items.SelectMany(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,7 @@ private void GenerateProperty(IProperty property, bool useDataAnnotations)
RemoveAnnotation(ref annotations, RelationalAnnotationNames.IsFixedLength);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.TableColumnMappings);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.ViewColumnMappings);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.RelationalOverrides);
RemoveAnnotation(ref annotations, ScaffoldingAnnotationNames.ColumnOrdinal);

if (!useDataAnnotations)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ private ColumnListComparer()
public int Compare(IReadOnlyList<IColumn> x, IReadOnlyList<IColumn> y)
{
var result = x.Count - y.Count;

if (result != 0)
{
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ private ColumnMappingBaseComparer()
/// </summary>
public int Compare(IColumnMappingBase x, IColumnMappingBase y)
{
var result = x.Column.IsNullable == y.Column.IsNullable
? 0
: x.Column.IsNullable ? 1 : -1;
var result = y.Column.IsNullable.CompareTo(x.Column.IsNullable);
if (result != 0)
{
return result;
Expand Down
26 changes: 23 additions & 3 deletions src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public static IModel Add(
var schema = entityType.GetSchema();
TableMapping lastMapping = null;
var mappedType = entityType;
SortedSet<TableMapping> tableMappings = null;
while (mappedType != null)
{
var mappedTable = mappedType.GetTableName();
Expand Down Expand Up @@ -147,7 +148,7 @@ public static IModel Add(
continue;
}

var tableMappings = entityType[RelationalAnnotationNames.TableMappings] as SortedSet<TableMapping>;
tableMappings = entityType[RelationalAnnotationNames.TableMappings] as SortedSet<TableMapping>;
if (tableMappings == null)
{
tableMappings = new SortedSet<TableMapping>(TableMappingBaseComparer.EntityTypeInstance);
Expand All @@ -160,14 +161,18 @@ public static IModel Add(
lastMapping = tableMapping;
}

// Re-add the mapping to update the order
tableMappings.Remove(lastMapping);
lastMapping.IsMainTableMapping = true;
tableMappings.Add(lastMapping);
}

var viewName = entityType.GetViewName();
if (viewName != null)
{
var schema = entityType.GetViewSchema();
ViewMapping lastMapping = null;
SortedSet<ViewMapping> viewMappings = null;
var mappedType = entityType;
while (mappedType != null)
{
Expand Down Expand Up @@ -232,7 +237,7 @@ public static IModel Add(
continue;
}

var viewMappings = entityType[RelationalAnnotationNames.ViewMappings] as SortedSet<ViewMapping>;
viewMappings = entityType[RelationalAnnotationNames.ViewMappings] as SortedSet<ViewMapping>;
if (viewMappings == null)
{
viewMappings = new SortedSet<ViewMapping>(TableMappingBaseComparer.EntityTypeInstance);
Expand All @@ -245,7 +250,10 @@ public static IModel Add(
lastMapping = viewMapping;
}

// Re-add the mapping to update the order
viewMappings.Remove(lastMapping);
lastMapping.IsMainTableMapping = true;
viewMappings.Add(lastMapping);
}
}

Expand Down Expand Up @@ -600,7 +608,19 @@ private static void PopulateRowInternalForeignKeys(TableBase table)
}
}

mainMapping.IsMainEntityTypeMapping = true;
// Re-add the mapping to update the order
if (mainMapping is TableMapping mainTableMapping)
{
((Table)mainMapping.Table).EntityTypeMappings.Remove(mainTableMapping);
mainMapping.IsMainEntityTypeMapping = true;
((Table)mainMapping.Table).EntityTypeMappings.Add(mainTableMapping);
}
else
{
((View)mainMapping.Table).EntityTypeMappings.Remove((ViewMapping)mainMapping);
mainMapping.IsMainEntityTypeMapping = true;
((View)mainMapping.Table).EntityTypeMappings.Add((ViewMapping)mainMapping);
}

if (referencingInternalForeignKeyMap != null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ private TableMappingBaseComparer(bool forEntityType)
public int Compare(ITableMappingBase x, ITableMappingBase y)
{
var result = _isForEntityType
? x.IsMainTableMapping.CompareTo(y.IsMainTableMapping)
: x.IsMainEntityTypeMapping.CompareTo(y.IsMainEntityTypeMapping);
? y.IsMainTableMapping.CompareTo(x.IsMainTableMapping)
: y.IsMainEntityTypeMapping.CompareTo(x.IsMainEntityTypeMapping);
if (result != 0)
{
return result;
Expand Down
131 changes: 95 additions & 36 deletions src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
Expand Down Expand Up @@ -68,10 +69,8 @@ public class MigrationsModelDiffer : IMigrationsModelDiffer

private IUpdateAdapter _sourceUpdateAdapter;
private IUpdateAdapter _targetUpdateAdapter;
private readonly Dictionary<ITable, SharedTableEntryMap<EntryMapping>> _sourceSharedTableEntryMaps =
new Dictionary<ITable, SharedTableEntryMap<EntryMapping>>();
private readonly Dictionary<ITable, SharedTableEntryMap<List<IUpdateEntry>>> _targetSharedTableEntryMaps =
new Dictionary<ITable, SharedTableEntryMap<List<IUpdateEntry>>>();
private readonly Dictionary<ITable, SharedIdentityMap> _sourceSharedTableEntryMaps =
new Dictionary<ITable, SharedIdentityMap>();

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down Expand Up @@ -1785,25 +1784,12 @@ protected virtual Dictionary<IEntityType, List<ITable>> DiffData(
if (targetTableMapping.IsMainTableMapping)
{
mainSourceTable = diffContext.FindSource(targetTable);
if (!_targetSharedTableEntryMaps.TryGetValue(targetTable, out var targetTableEntryMappingMap))
{
targetTableEntryMappingMap = SharedTableEntryMap<List<IUpdateEntry>>.CreateSharedTableEntryMapFactory(
targetTable,
_targetUpdateAdapter)
((_, __, ___) => new List<IUpdateEntry>());
_targetSharedTableEntryMaps.Add(targetTable, targetTableEntryMappingMap);
}

foreach (var targetSeed in targetEntityType.GetSeedData())
{
var targetEntry = GetEntry(targetSeed, targetEntityType, _targetUpdateAdapter);
var targetEntries = targetTableEntryMappingMap.GetOrAddValue(targetEntry);
targetEntries.Add(targetEntry);
}

continue;
}

Check.DebugAssert(mainSourceTable != null, "mainSourceTable is null");

var newMapping = true;
var sourceTable = diffContext.FindSource(targetTable);
if (sourceTable != null)
Expand Down Expand Up @@ -1853,6 +1839,8 @@ protected virtual Dictionary<IEntityType, List<ITable>> DiffData(
continue;
}

Check.DebugAssert(mainSourceTable != null, "mainSourceTable is null");

var targetTable = diffContext.FindTarget(sourceTable);
var removedMapping = true;
if (targetTable != null
Expand Down Expand Up @@ -1886,20 +1874,26 @@ protected virtual Dictionary<IEntityType, List<ITable>> DiffData(
continue;
}

// If table sharing is being used find the main table of the principal entity type
var mainSourceEntityType = sourceEntityType;
var mainPrincipalSourceTable = mainSourceTable;
while (mainSourceTable.GetRowInternalForeignKeys(mainSourceEntityType).Any())
{
mainSourceEntityType = mainPrincipalSourceTable.EntityTypeMappings.First(m => m.IsMainEntityTypeMapping).EntityType;
mainPrincipalSourceTable = mainSourceEntityType.GetTableMappings().First(m => m.IsMainTableMapping).Table;
}

foreach (var sourceSeed in sourceEntityType.GetSeedData())
{
var sourceEntry = GetEntry(sourceSeed, sourceEntityType, _sourceUpdateAdapter);

if (!_sourceSharedTableEntryMaps.TryGetValue(mainSourceTable, out var sourceTableEntryMappingMap))
if (!_sourceSharedTableEntryMaps.TryGetValue(mainPrincipalSourceTable, out var sourceTableEntryMappingMap))
{
sourceTableEntryMappingMap = SharedTableEntryMap<EntryMapping>.CreateSharedTableEntryMapFactory(
mainSourceTable,
_sourceUpdateAdapter)
((_, __, ___) => new EntryMapping());
_sourceSharedTableEntryMaps.Add(mainSourceTable, sourceTableEntryMappingMap);
sourceTableEntryMappingMap = new SharedIdentityMap(_sourceUpdateAdapter);
_sourceSharedTableEntryMaps.Add(mainPrincipalSourceTable, sourceTableEntryMappingMap);
}

var entryMapping = sourceTableEntryMappingMap.GetOrAddValue(sourceEntry);
var entryMapping = sourceTableEntryMappingMap.GetOrAddValue(sourceEntry, mainSourceTable);
entryMapping.SourceEntries.Add(sourceEntry);

if (targetKeyMap == null)
Expand All @@ -1925,28 +1919,28 @@ protected virtual Dictionary<IEntityType, List<ITable>> DiffData(
}

var entry = _targetUpdateAdapter.TryGetEntry(targetKey, targetKeyValues);
if (entry == null
|| !_targetSharedTableEntryMaps.TryGetValue(targetTable, out var targetTableEntryMappingMap))
if (entry == null)
{
continue;
}

foreach (var targetEntry in targetTableEntryMappingMap.GetOrAddValue(entry))
if (entryMapping.TargetEntries.Add(entry))
{
if (!entryMapping.TargetEntries.Add(targetEntry))
if (entry.EntityState != EntityState.Added)
{
Check.DebugAssert(false, "All entries must be in added state at this point");
continue;
}

foreach (var targetProperty in targetEntry.EntityType.GetProperties())
foreach (var targetProperty in entry.EntityType.GetProperties())
{
if (targetProperty.GetAfterSaveBehavior() == PropertySaveBehavior.Save)
{
targetEntry.SetOriginalValue(targetProperty, targetProperty.ClrType.GetDefaultValue());
entry.SetOriginalValue(targetProperty, targetProperty.ClrType.GetDefaultValue());
}
}

targetEntry.EntityState = EntityState.Unchanged;
entry.EntityState = EntityState.Unchanged;
}

if (entryMapping.RecreateRow)
Expand Down Expand Up @@ -2132,7 +2126,6 @@ protected virtual IEnumerable<MigrationOperation> GetDataOperations(
}

_sourceSharedTableEntryMaps.Clear();
_targetSharedTableEntryMaps.Clear();

var dataOperations = GetDataOperations(forSource: true, changedTableMappings, entriesWithRemovedMappings, diffContext)
.Concat(GetDataOperations(forSource: false, changedTableMappings, entriesWithRemovedMappings, diffContext));
Expand Down Expand Up @@ -2221,8 +2214,12 @@ private IEnumerable<MigrationOperation> GetDataOperations(
batchInsertOperation = null;
}

// There shouldn't be any updates using the source model
Check.DebugAssert(!forSource, "Update using the source model");
if (forSource)
{
// There shouldn't be any updates using the source model
Check.DebugAssert(false, "Update using the source model");
break;
}

if (command.Entries.Any(en => changedTableMappings.TryGetValue(en.EntityType, out var newTables)
&& newTables.Any(t => t.Name == command.TableName && t.Schema == command.Schema)))
Expand Down Expand Up @@ -2486,6 +2483,68 @@ private sealed class EntryMapping
public bool RecreateRow { get; set; }
}

private sealed class SharedIdentityMap
{
private readonly IUpdateAdapter _updateAdapter;
private readonly Dictionary<IUpdateEntry, EntryMapping> _entryValueMap
= new Dictionary<IUpdateEntry, EntryMapping>();

public SharedIdentityMap(IUpdateAdapter updateAdapter)
{
_updateAdapter = updateAdapter;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public IEnumerable<EntryMapping> Values => _entryValueMap.Values;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public EntryMapping GetOrAddValue([NotNull] IUpdateEntry entry, ITable table)
{
var mainEntry = GetMainEntry(entry, table);
if (_entryValueMap.TryGetValue(mainEntry, out var entryMapping))
{
return entryMapping;
}

entryMapping = new EntryMapping();
_entryValueMap.Add(mainEntry, entryMapping);

return entryMapping;
}

private IUpdateEntry GetMainEntry(IUpdateEntry entry, ITable table)
{
var entityType = entry.EntityType;
var foreignKeys = table.GetRowInternalForeignKeys(entityType);
foreach (var foreignKey in foreignKeys)
{
var principalEntry = _updateAdapter.FindPrincipal(entry, foreignKey);
if (principalEntry != null)
{
return GetMainEntry(principalEntry, table);
}
}

var mainTable = entry.EntityType.GetTableMappings().First(m => m.IsMainTableMapping).Table;
if (mainTable != table)
{
return GetMainEntry(entry, mainTable);
}

return entry;
}
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ protected override ShapedQueryExpression TranslateOfType(ShapedQueryExpression s
var derivedType = entityType.GetDerivedTypes().SingleOrDefault(et => et.ClrType == resultType);
if (derivedType != null)
{
if (!derivedType.GetIsDiscriminatorMappingComplete()
if (!derivedType.GetRootType().GetIsDiscriminatorMappingComplete()
|| !derivedType.GetAllBaseTypesInclusiveAscending()
.All(e => (e == derivedType || e.IsAbstract()) && !HasSiblings(e)))
{
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore.Relational/Query/SqlExpressionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ private SqlExpression GenerateJoinPredicate(

private bool AddDiscriminatorCondition(SelectExpression selectExpression, IEntityType entityType)
{
if (entityType.GetIsDiscriminatorMappingComplete()
if (entityType.GetRootType().GetIsDiscriminatorMappingComplete()
&& entityType.GetAllBaseTypesInclusiveAscending()
.All(e => (e == entityType || e.IsAbstract()) && !HasSiblings(e)))
{
Expand Down
Loading

0 comments on commit aee7634

Please sign in to comment.