Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TPC support to update pipeline #27737

Merged
merged 4 commits into from
Apr 5, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) },
AndriySvyryd marked this conversation as resolved.
Show resolved Hide resolved
{ 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)
AndriySvyryd marked this conversation as resolved.
Show resolved Hide resolved
{
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>
AndriySvyryd marked this conversation as resolved.
Show resolved Hide resolved
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
AndriySvyryd marked this conversation as resolved.
Show resolved Hide resolved
=> 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