Skip to content

Commit

Permalink
Add TPC support to update pipeline
Browse files Browse the repository at this point in the history
Switch the update pipeline to use the relational model for command ordering
Add provider value comparer

Part of #3170
  • Loading branch information
AndriySvyryd committed Apr 1, 2022
1 parent b7fefca commit c43f51b
Show file tree
Hide file tree
Showing 67 changed files with 2,575 additions and 859 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ public class EntityFrameworkRelationalServicesBuilder : EntityFrameworkServicesB
public static readonly IDictionary<Type, ServiceCharacteristics> RelationalServices
= new Dictionary<Type, ServiceCharacteristics>
{
{ typeof(IKeyValueIndexFactorySource), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IRowKeyValueFactoryFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IRowForeignKeyValueFactoryFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IRowIndexValueFactoryFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IParameterNameGeneratorFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IComparer<IReadOnlyModificationCommand>), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IMigrationsIdGenerator), new ServiceCharacteristics(ServiceLifetime.Singleton) },
Expand Down Expand Up @@ -125,7 +127,9 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
TryAdd<IParameterNameGeneratorFactory, ParameterNameGeneratorFactory>();
TryAdd<IComparer<IReadOnlyModificationCommand>, ModificationCommandComparer>();
TryAdd<IMigrationsIdGenerator, MigrationsIdGenerator>();
TryAdd<IKeyValueIndexFactorySource, KeyValueIndexFactorySource>();
TryAdd<IRowKeyValueFactoryFactory, RowKeyValueFactoryFactory>();
TryAdd<IRowForeignKeyValueFactoryFactory, RowForeignKeyValueFactoryFactory>();
TryAdd<IRowIndexValueFactoryFactory, RowIndexValueFactoryFactory>();
TryAdd<IModelCustomizer, RelationalModelCustomizer>();
TryAdd<IModelRuntimeInitializer, RelationalModelRuntimeInitializer>();
TryAdd<IRelationalAnnotationProvider, RelationalAnnotationProvider>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Update.Internal;

namespace Microsoft.EntityFrameworkCore.Infrastructure;

/// <summary>
Expand Down Expand Up @@ -45,7 +47,40 @@ public sealed record RelationalModelDependencies
/// the constructor at any point in this process.
/// </remarks>
[EntityFrameworkInternal]
public RelationalModelDependencies()
public RelationalModelDependencies(
IRowKeyValueFactoryFactory rowKeyValueFactoryFactory,
IRowForeignKeyValueFactoryFactory foreignKeyRowValueFactorySource,
IRowIndexValueFactoryFactory rowIndexValueFactoryFactory)
{
RowKeyValueFactoryFactory = rowKeyValueFactoryFactory;
RowForeignKeyValueFactoryFactory = foreignKeyRowValueFactorySource;
RowIndexValueFactoryFactory = rowIndexValueFactoryFactory;
}

/// <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>
[EntityFrameworkInternal]
public IRowKeyValueFactoryFactory RowKeyValueFactoryFactory { get; init; }

/// <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>
[EntityFrameworkInternal]
public IRowForeignKeyValueFactoryFactory RowForeignKeyValueFactoryFactory { get; init; }

/// <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>
[EntityFrameworkInternal]
public IRowIndexValueFactoryFactory RowIndexValueFactoryFactory { get; init; }
}
40 changes: 29 additions & 11 deletions src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -559,10 +559,10 @@ protected virtual void ValidateSharedViewCompatibility(
mappedTypes.Add(entityType);
}

foreach (var (table, mappedTypes) in views)
foreach (var (view, mappedTypes) in views)
{
ValidateSharedViewCompatibility(mappedTypes, table.Name, table.Schema, logger);
ValidateSharedColumnsCompatibility(mappedTypes, table, logger);
ValidateSharedViewCompatibility(mappedTypes, view.Name, view.Schema, logger);
ValidateSharedColumnsCompatibility(mappedTypes, view, logger);
}
}

Expand Down Expand Up @@ -866,10 +866,12 @@ protected virtual void ValidateCompatible(
storeObject.DisplayName()));
}

var typeMapping = property.GetRelationalTypeMapping();
var duplicateTypeMapping = duplicateProperty.GetRelationalTypeMapping();
var currentTypeString = property.GetColumnType(storeObject)
?? property.GetRelationalTypeMapping().StoreType;
?? typeMapping.StoreType;
var previousTypeString = duplicateProperty.GetColumnType(storeObject)
?? duplicateProperty.GetRelationalTypeMapping().StoreType;
?? duplicateTypeMapping.StoreType;
if (!string.Equals(currentTypeString, previousTypeString, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
Expand All @@ -884,6 +886,22 @@ protected virtual void ValidateCompatible(
currentTypeString));
}

var currentProviderType = typeMapping.Converter?.ProviderClrType ?? typeMapping.ClrType;
var previousProviderType = duplicateTypeMapping.Converter?.ProviderClrType ?? duplicateTypeMapping.ClrType;
if (currentProviderType != previousProviderType)
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameProviderTypeMismatch(
duplicateProperty.DeclaringEntityType.DisplayName(),
duplicateProperty.Name,
property.DeclaringEntityType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName(),
previousProviderType.ShortDisplayName(),
currentProviderType.ShortDisplayName()));
}

var currentComputedColumnSql = property.GetComputedColumnSql(storeObject) ?? "";
var previousComputedColumnSql = duplicateProperty.GetComputedColumnSql(storeObject) ?? "";
if (!currentComputedColumnSql.Equals(previousComputedColumnSql, StringComparison.OrdinalIgnoreCase))
Expand Down Expand Up @@ -1340,8 +1358,8 @@ protected override void ValidateInheritanceMapping(
RelationalStrings.NonTphMappingStrategy(mappingStrategy, entityType.DisplayName()));
}

ValidateTPHMapping(entityType, forTables: false);
ValidateTPHMapping(entityType, forTables: true);
ValidateTphMapping(entityType, forTables: false);
ValidateTphMapping(entityType, forTables: true);
ValidateDiscriminatorValues(entityType);
}
else
Expand All @@ -1362,8 +1380,8 @@ protected override void ValidateInheritanceMapping(
RelationalStrings.KeylessMappingStrategy(mappingStrategy ?? RelationalAnnotationNames.TptMappingStrategy, entityType.DisplayName()));
}

ValidateNonTPHMapping(entityType, forTables: false);
ValidateNonTPHMapping(entityType, forTables: true);
ValidateNonTphMapping(entityType, forTables: false);
ValidateNonTphMapping(entityType, forTables: true);
}
}
}
Expand All @@ -1387,7 +1405,7 @@ protected virtual void ValidateMappingStrategy(string? mappingStrategy, IEntityT
};
}

private static void ValidateNonTPHMapping(IEntityType rootEntityType, bool forTables)
private static void ValidateNonTphMapping(IEntityType rootEntityType, bool forTables)
{
var derivedTypes = new Dictionary<(string, string?), IEntityType>();
foreach (var entityType in rootEntityType.GetDerivedTypesInclusive())
Expand All @@ -1413,7 +1431,7 @@ private static void ValidateNonTPHMapping(IEntityType rootEntityType, bool forTa
}
}

private static void ValidateTPHMapping(IEntityType rootEntityType, bool forTables)
private static void ValidateTphMapping(IEntityType rootEntityType, bool forTables)
{
string? firstName = null;
string? firstSchema = null;
Expand Down
21 changes: 19 additions & 2 deletions src/EFCore.Relational/Metadata/IColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ public virtual bool TryGetDefaultValue(out object? defaultValue)
continue;
}

var converter = property.GetValueConverter() ?? PropertyMappings.First().TypeMapping.Converter;

var converter = property.GetValueConverter() ?? mapping.TypeMapping.Converter;
if (converter != null)
{
defaultValue = converter.ConvertToProvider(defaultValue);
Expand Down Expand Up @@ -148,6 +147,24 @@ public virtual string? Collation
=> PropertyMappings.First().Property
.GetCollation(StoreObjectIdentifier.Table(Table.Name, Table.Schema));

/// <summary>
/// Returns the property mapping for the given entity type.
/// </summary>
/// <param name="entityType"> An entity type. </param>
/// <returns> The property mapping or <see langword="null"/> if not found. </returns>
public virtual IColumnMapping? FindColumnMapping(IReadOnlyEntityType entityType)
{
foreach (var mapping in PropertyMappings)
{
if (mapping.Property.DeclaringEntityType.IsAssignableFrom(entityType))
{
return mapping;
}
}

return null;
}

/// <summary>
/// <para>
/// Creates a human-readable representation of the given metadata.
Expand Down
5 changes: 5 additions & 0 deletions src/EFCore.Relational/Metadata/IColumnBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ public interface IColumnBase : IAnnotatable
/// </summary>
string StoreType { get; }

/// <summary>
/// Gets the provider type.
/// </summary>
Type ProviderClrType { get; }

/// <summary>
/// Gets the value indicating whether the column can contain NULL.
/// </summary>
Expand Down
7 changes: 6 additions & 1 deletion src/EFCore.Relational/Metadata/IForeignKeyConstraint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ public interface IForeignKeyConstraint : IAnnotatable
/// <summary>
/// Gets the columns that are referenced by the foreign key constraint.
/// </summary>
IReadOnlyList<IColumn> PrincipalColumns { get; }
IReadOnlyList<IColumn> PrincipalColumns => PrincipalUniqueConstraint.Columns;

/// <summary>
/// Gets the unique constraint on the columns referenced by the foreign key constraint.
/// </summary>
IUniqueConstraint PrincipalUniqueConstraint { get; }

/// <summary>
/// Gets the action to be performed when the referenced row is deleted.
Expand Down
5 changes: 5 additions & 0 deletions src/EFCore.Relational/Metadata/ITable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public interface ITable : ITableBase
/// </summary>
IEnumerable<IForeignKeyConstraint> ForeignKeyConstraints { get; }

/// <summary>
/// Gets the foreign key constraints referencing this table.
/// </summary>
IEnumerable<IForeignKeyConstraint> ReferencingForeignKeyConstraints { get; }

/// <summary>
/// Gets the unique constraints including the primary key for this table.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions src/EFCore.Relational/Metadata/ITableBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ public interface ITableBase : IAnnotatable
/// </summary>
string? Schema { get; }

/// <summary>
/// Gets the schema-qualified name of the table in the database.
/// </summary>
string SchemaQualifiedTableName
=> Schema == null ? Name : Schema + "." + Name;

/// <summary>
/// Gets the database model.
/// </summary>
Expand Down
17 changes: 17 additions & 0 deletions src/EFCore.Relational/Metadata/Internal/Column.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Update.Internal;

namespace Microsoft.EntityFrameworkCore.Metadata.Internal;

/// <summary>
Expand All @@ -11,6 +14,9 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
/// </summary>
public class Column : ColumnBase, IColumn
{
// Warning: Never access these fields directly as access needs to be thread-safe
private ColumnAccessors? _accessors;

/// <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 All @@ -31,6 +37,17 @@ public Column(string name, string type, Table table)
public new virtual Table Table
=> (Table)base.Table;

/// <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 virtual ColumnAccessors Accessors
=> NonCapturingLazyInitializer.EnsureInitialized(
ref _accessors, this, static column =>
ColumnAccessorsFactory.Create(column));

/// <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
24 changes: 24 additions & 0 deletions src/EFCore.Relational/Metadata/Internal/ColumnBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
/// </summary>
public class ColumnBase : Annotatable, IColumnBase
{
private Type? _providerClrType;

/// <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 Expand Up @@ -65,6 +67,28 @@ public override bool IsReadOnly
/// </summary>
public virtual bool IsNullable { get; set; }

/// <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 virtual Type ProviderClrType
{
get
{
if (_providerClrType != null)
{
return _providerClrType;
}

var typeMapping = PropertyMappings.First().TypeMapping;
var providerType = typeMapping.Converter?.ProviderClrType ?? typeMapping.ClrType;

return _providerClrType = providerType;
}
}

/// <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
34 changes: 31 additions & 3 deletions src/EFCore.Relational/Metadata/Internal/ForeignKeyConstraint.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Update.Internal;

namespace Microsoft.EntityFrameworkCore.Metadata.Internal;

/// <summary>
Expand All @@ -22,14 +25,14 @@ public ForeignKeyConstraint(
Table table,
Table principalTable,
IReadOnlyList<Column> columns,
IReadOnlyList<Column> principalColumns,
UniqueConstraint principalUniqueConstraint,
ReferentialAction onDeleteAction)
{
Name = name;
Table = table;
PrincipalTable = principalTable;
Columns = columns;
PrincipalColumns = principalColumns;
PrincipalUniqueConstraint = principalUniqueConstraint;
OnDeleteAction = onDeleteAction;
}

Expand Down Expand Up @@ -74,7 +77,15 @@ public ForeignKeyConstraint(
/// 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 virtual IReadOnlyList<Column> PrincipalColumns { get; }
public virtual IReadOnlyList<Column> PrincipalColumns => PrincipalUniqueConstraint.Columns;

/// <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 virtual UniqueConstraint PrincipalUniqueConstraint { get; }

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -88,6 +99,19 @@ public override bool IsReadOnly
/// <inheritdoc />
public virtual ReferentialAction OnDeleteAction { get; set; }

private IRowForeignKeyValueFactory? _foreignKeyRowValueFactory;

/// <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 virtual IRowForeignKeyValueFactory GetRowForeignKeyValueFactory()
=> NonCapturingLazyInitializer.EnsureInitialized(
ref _foreignKeyRowValueFactory, this,
static constraint => constraint.Table.Model.Model.GetRelationalDependencies().RowForeignKeyValueFactoryFactory.Create(constraint));

/// <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 Expand Up @@ -116,4 +140,8 @@ IReadOnlyList<IColumn> IForeignKeyConstraint.Columns
/// <inheritdoc />
IReadOnlyList<IColumn> IForeignKeyConstraint.PrincipalColumns
=> PrincipalColumns;

/// <inheritdoc />
IUniqueConstraint IForeignKeyConstraint.PrincipalUniqueConstraint
=> PrincipalUniqueConstraint;
}
Loading

0 comments on commit c43f51b

Please sign in to comment.