Skip to content

Commit

Permalink
Add runtime annotation support to model.
Browse files Browse the repository at this point in the history
  Runtime annotations can only be added to a read-only model and ModelRuntimeInitializer is the service that adds the required ones.
Model validation has been moved to ModelRuntimeInitializer.

Fixes #22031
  • Loading branch information
AndriySvyryd committed Jan 20, 2021
1 parent 5d53796 commit 9c1117f
Show file tree
Hide file tree
Showing 128 changed files with 1,890 additions and 932 deletions.
29 changes: 6 additions & 23 deletions src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

namespace Microsoft.EntityFrameworkCore.Migrations.Internal
Expand All @@ -26,7 +24,7 @@ public class SnapshotModelProcessor : ISnapshotModelProcessor
{
private readonly IOperationReporter _operationReporter;
private readonly HashSet<string> _relationalNames;
private readonly IConventionSetBuilder _conventionSetBuilder;
private readonly IModelRuntimeInitializer _modelRuntimeInitializer;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -36,15 +34,15 @@ public class SnapshotModelProcessor : ISnapshotModelProcessor
/// </summary>
public SnapshotModelProcessor(
[NotNull] IOperationReporter operationReporter,
[NotNull] IConventionSetBuilder conventionSetBuilder)
[NotNull] IModelRuntimeInitializer modelRuntimeInitializer)
{
_operationReporter = operationReporter;
_relationalNames = new HashSet<string>(
typeof(RelationalAnnotationNames)
.GetRuntimeFields()
.Where(p => p.Name != nameof(RelationalAnnotationNames.Prefix))
.Select(p => ((string)p.GetValue(null)).Substring(RelationalAnnotationNames.Prefix.Length - 1)));
_conventionSetBuilder = conventionSetBuilder;
_modelRuntimeInitializer = modelRuntimeInitializer;
}

/// <summary>
Expand Down Expand Up @@ -82,27 +80,12 @@ public virtual IModel Process(IModel model)
}
}

if (model is IConventionModel conventionModel)
if (model is IMutableModel mutableModel)
{
var conventionSet = _conventionSetBuilder.CreateConventionSet();

var typeMappingConvention = conventionSet.ModelFinalizingConventions.OfType<TypeMappingConvention>().FirstOrDefault();
if (typeMappingConvention != null)
{
typeMappingConvention.ProcessModelFinalizing(conventionModel.Builder, null);
}

var relationalModelConvention =
conventionSet.ModelFinalizedConventions.OfType<RelationalModelConvention>().FirstOrDefault();
if (relationalModelConvention != null)
{
model = relationalModelConvention.ProcessModelFinalized(conventionModel);
}
model = mutableModel.FinalizeModel();
}

return model is IMutableModel mutableModel
? mutableModel.FinalizeModel()
: model;
return _modelRuntimeInitializer.Initialize(model, validationLogger: null);
}

private void ProcessCollection(IEnumerable<IAnnotatable> metadata, string version)
Expand Down
14 changes: 0 additions & 14 deletions src/EFCore.Relational/Design/AnnotationCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,9 @@ public class AnnotationCodeGenerator : IAnnotationCodeGenerator
{
private static readonly ISet<string> _ignoredRelationalAnnotations = new HashSet<string>
{
RelationalAnnotationNames.RelationalModel,
RelationalAnnotationNames.CheckConstraints,
RelationalAnnotationNames.Sequences,
RelationalAnnotationNames.DbFunctions,
RelationalAnnotationNames.DefaultMappings,
RelationalAnnotationNames.DefaultColumnMappings,
RelationalAnnotationNames.TableMappings,
RelationalAnnotationNames.TableColumnMappings,
RelationalAnnotationNames.ViewMappings,
RelationalAnnotationNames.ViewColumnMappings,
RelationalAnnotationNames.FunctionMappings,
RelationalAnnotationNames.FunctionColumnMappings,
RelationalAnnotationNames.SqlQueryMappings,
RelationalAnnotationNames.SqlQueryColumnMappings,
RelationalAnnotationNames.ForeignKeyMappings,
RelationalAnnotationNames.TableIndexMappings,
RelationalAnnotationNames.UniqueConstraintMappings,
RelationalAnnotationNames.RelationalOverrides
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ public static string GetSchemaQualifiedViewName([NotNull] this IEntityType entit
/// <param name="entityType"> The entity type to get the table mappings for. </param>
/// <returns> The tables to which the entity type is mapped. </returns>
public static IEnumerable<ITableMappingBase> GetDefaultMappings([NotNull] this IEntityType entityType)
=> (IEnumerable<ITableMappingBase>)entityType[RelationalAnnotationNames.DefaultMappings]
=> (IEnumerable<ITableMappingBase>)entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.DefaultMappings)
?? Array.Empty<ITableMappingBase>();

/// <summary>
Expand All @@ -254,7 +254,7 @@ public static IEnumerable<ITableMappingBase> GetDefaultMappings([NotNull] this I
/// <param name="entityType"> The entity type to get the table mappings for. </param>
/// <returns> The tables to which the entity type is mapped. </returns>
public static IEnumerable<ITableMapping> GetTableMappings([NotNull] this IEntityType entityType)
=> (IEnumerable<ITableMapping>)entityType[RelationalAnnotationNames.TableMappings]
=> (IEnumerable<ITableMapping>)entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableMappings)
?? Array.Empty<ITableMapping>();

/// <summary>
Expand Down Expand Up @@ -417,7 +417,7 @@ public static string SetViewSchema(
/// <param name="entityType"> The entity type to get the view mappings for. </param>
/// <returns> The views to which the entity type is mapped. </returns>
public static IEnumerable<IViewMapping> GetViewMappings([NotNull] this IEntityType entityType)
=> (IEnumerable<IViewMapping>)entityType[RelationalAnnotationNames.ViewMappings]
=> (IEnumerable<IViewMapping>)entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.ViewMappings)
?? Array.Empty<IViewMapping>();

/// <summary>
Expand Down Expand Up @@ -491,7 +491,7 @@ public static string SetSqlQuery(
/// <param name="entityType"> The entity type to get the function mappings for. </param>
/// <returns> The functions to which the entity type is mapped. </returns>
public static IEnumerable<ISqlQueryMapping> GetSqlQueryMappings([NotNull] this IEntityType entityType)
=> (IEnumerable<ISqlQueryMapping>)entityType[RelationalAnnotationNames.SqlQueryMappings]
=> (IEnumerable<ISqlQueryMapping>)entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.SqlQueryMappings)
?? Array.Empty<ISqlQueryMapping>();

/// <summary>
Expand Down Expand Up @@ -556,7 +556,7 @@ public static string SetFunctionName(
/// <param name="entityType"> The entity type to get the function mappings for. </param>
/// <returns> The functions to which the entity type is mapped. </returns>
public static IEnumerable<IFunctionMapping> GetFunctionMappings([NotNull] this IEntityType entityType)
=> (IEnumerable<IFunctionMapping>)entityType[RelationalAnnotationNames.FunctionMappings]
=> (IEnumerable<IFunctionMapping>)entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.FunctionMappings)
?? Array.Empty<IFunctionMapping>();

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public static string SetConstraintName(
/// <param name="foreignKey"> The foreign key. </param>
/// <returns> The foreign key constraints to which the foreign key is mapped. </returns>
public static IEnumerable<IForeignKeyConstraint> GetMappedConstraints([NotNull] this IForeignKey foreignKey)
=> (IEnumerable<IForeignKeyConstraint>)foreignKey[RelationalAnnotationNames.ForeignKeyMappings]
=> (IEnumerable<IForeignKeyConstraint>)foreignKey.FindRuntimeAnnotationValue(RelationalAnnotationNames.ForeignKeyMappings)
?? Enumerable.Empty<IForeignKeyConstraint>();

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ public static string SetFilter([NotNull] this IConventionIndex index, [CanBeNull
/// <param name="index"> The index. </param>
/// <returns> The table indexes to which the index is mapped. </returns>
public static IEnumerable<ITableIndex> GetMappedTableIndexes([NotNull] this IIndex index)
=> (IEnumerable<ITableIndex>)index[RelationalAnnotationNames.TableIndexMappings]
=> (IEnumerable<ITableIndex>)index.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableIndexMappings)
?? Enumerable.Empty<ITableIndex>();

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public static string SetName([NotNull] this IConventionKey key, [CanBeNull] stri
/// <param name="key"> The key. </param>
/// <returns> The unique constraints to which the key is mapped. </returns>
public static IEnumerable<IUniqueConstraint> GetMappedConstraints([NotNull] this IKey key)
=> (IEnumerable<IUniqueConstraint>)key[RelationalAnnotationNames.UniqueConstraintMappings]
=> (IEnumerable<IUniqueConstraint>)key.FindRuntimeAnnotationValue(RelationalAnnotationNames.UniqueConstraintMappings)
?? Enumerable.Empty<IUniqueConstraint>();

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions src/EFCore.Relational/Extensions/RelationalModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,10 @@ public static void SetDefaultSchema([NotNull] this IMutableModel model, [CanBeNu
/// <returns> The database model. </returns>
public static IRelationalModel GetRelationalModel([NotNull] this IModel model)
{
var databaseModel = (IRelationalModel?)model[RelationalAnnotationNames.RelationalModel];
var databaseModel = (IRelationalModel?)model.FindRuntimeAnnotationValue(RelationalAnnotationNames.RelationalModel);
if (databaseModel == null)
{
throw new InvalidOperationException(RelationalStrings.DatabaseModelMissing);
throw new InvalidOperationException(CoreStrings.ModelNotFinalized(nameof(GetRelationalModel)));
}

return databaseModel;
Expand Down
10 changes: 5 additions & 5 deletions src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ public static string SetColumnType(
/// <param name="property"> The property. </param>
/// <returns> The default columns to which the property would be mapped. </returns>
public static IEnumerable<IColumnMappingBase> GetDefaultColumnMappings([NotNull] this IProperty property)
=> (IEnumerable<IColumnMappingBase>)property[RelationalAnnotationNames.DefaultColumnMappings]
=> (IEnumerable<IColumnMappingBase>)property.FindRuntimeAnnotationValue(RelationalAnnotationNames.DefaultColumnMappings)
?? Enumerable.Empty<IColumnMappingBase>();

/// <summary>
Expand All @@ -375,7 +375,7 @@ public static IEnumerable<IColumnMappingBase> GetDefaultColumnMappings([NotNull]
/// <param name="property"> The property. </param>
/// <returns> The table columns to which the property is mapped. </returns>
public static IEnumerable<IColumnMapping> GetTableColumnMappings([NotNull] this IProperty property)
=> (IEnumerable<IColumnMapping>)property[RelationalAnnotationNames.TableColumnMappings]
=> (IEnumerable<IColumnMapping>)property.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableColumnMappings)
?? Enumerable.Empty<IColumnMapping>();

/// <summary>
Expand All @@ -384,7 +384,7 @@ public static IEnumerable<IColumnMapping> GetTableColumnMappings([NotNull] this
/// <param name="property"> The property. </param>
/// <returns> The view columns to which the property is mapped. </returns>
public static IEnumerable<IViewColumnMapping> GetViewColumnMappings([NotNull] this IProperty property)
=> (IEnumerable<IViewColumnMapping>)property[RelationalAnnotationNames.ViewColumnMappings]
=> (IEnumerable<IViewColumnMapping>)property.FindRuntimeAnnotationValue(RelationalAnnotationNames.ViewColumnMappings)
?? Enumerable.Empty<IViewColumnMapping>();

/// <summary>
Expand All @@ -393,7 +393,7 @@ public static IEnumerable<IViewColumnMapping> GetViewColumnMappings([NotNull] th
/// <param name="property"> The property. </param>
/// <returns> The SQL query columns to which the property is mapped. </returns>
public static IEnumerable<ISqlQueryColumnMapping> GetSqlQueryColumnMappings([NotNull] this IProperty property)
=> (IEnumerable<ISqlQueryColumnMapping>)property[RelationalAnnotationNames.SqlQueryColumnMappings]
=> (IEnumerable<ISqlQueryColumnMapping>)property.FindRuntimeAnnotationValue(RelationalAnnotationNames.SqlQueryColumnMappings)
?? Enumerable.Empty<ISqlQueryColumnMapping>();

/// <summary>
Expand All @@ -402,7 +402,7 @@ public static IEnumerable<ISqlQueryColumnMapping> GetSqlQueryColumnMappings([Not
/// <param name="property"> The property. </param>
/// <returns> The function columns to which the property is mapped. </returns>
public static IEnumerable<IFunctionColumnMapping> GetFunctionColumnMappings([NotNull] this IProperty property)
=> (IEnumerable<IFunctionColumnMapping>)property[RelationalAnnotationNames.FunctionColumnMappings]
=> (IEnumerable<IFunctionColumnMapping>)property.FindRuntimeAnnotationValue(RelationalAnnotationNames.FunctionColumnMappings)
?? Enumerable.Empty<IFunctionColumnMapping>();

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
TryAdd<IMigrationsIdGenerator, MigrationsIdGenerator>();
TryAdd<IKeyValueIndexFactorySource, KeyValueIndexFactorySource>();
TryAdd<IModelCustomizer, RelationalModelCustomizer>();
TryAdd<IModelRuntimeInitializer, RelationalModelRuntimeInitializer>();
TryAdd<IRelationalAnnotationProvider, RelationalAnnotationProvider>();
TryAdd<IMigrationsAnnotationProvider, MigrationsAnnotationProvider>();
TryAdd<IModelValidator, RelationalModelValidator>();
Expand Down Expand Up @@ -198,6 +199,8 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
.AddDependencySingleton<RelationalEvaluatableExpressionFilterDependencies>()
.AddDependencySingleton<RelationalQueryTranslationPreprocessorDependencies>()
.AddDependencySingleton<RelationalParameterBasedSqlProcessorDependencies>()
.AddDependencySingleton<RelationalModelDependencies>()
.AddDependencySingleton<RelationalModelRuntimeInitializerDependencies>()
.AddDependencyScoped<MigrationsSqlGeneratorDependencies>()
.AddDependencyScoped<RelationalConventionSetBuilderDependencies>()
.AddDependencyScoped<ModificationCommandBatchFactoryDependencies>()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;

#nullable enable

namespace Microsoft.EntityFrameworkCore.Infrastructure
{
/// <summary>
/// <para>
/// Service dependencies parameter class for <see cref="RelationalModelRuntimeInitializer" />
/// </para>
/// <para>
/// This type is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// <para>
/// Do not construct instances of this class directly from either provider or application code as the
/// constructor signature may change as new dependencies are added. Instead, use this type in
/// your constructor so that an instance will be created and injected automatically by the
/// dependency injection container. To create an instance with some dependent services replaced,
/// first resolve the object from the dependency injection container, then replace selected
/// services using the 'With...' methods. Do not call the constructor at any point in this process.
/// </para>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Singleton" />.
/// This means a single instance of each service is used by many <see cref="DbContext" /> instances.
/// The implementation must be thread-safe.
/// This service cannot depend on services registered as <see cref="ServiceLifetime.Scoped" />.
/// </para>
/// </summary>
public sealed record RelationalModelRuntimeInitializerDependencies
{
/// <summary>
/// <para>
/// Creates the service dependencies parameter object for a <see cref="RelationalModelRuntimeInitializer" />.
/// </para>
/// <para>
/// 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.
/// </para>
/// <para>
/// Do not call this constructor directly from either provider or application code as it may change
/// as new dependencies are added. Instead, use this type in your constructor so that an instance
/// will be created and injected automatically by the dependency injection container. To create
/// an instance with some dependent services replaced, first resolve the object from the dependency
/// injection container, then replace selected services using the 'With...' methods. Do not call
/// the constructor at any point in this process.
/// </para>
/// </summary>
[EntityFrameworkInternal]
public RelationalModelRuntimeInitializerDependencies(
[NotNull] RelationalModelDependencies singletonModelDependencies,
[NotNull] IRelationalAnnotationProvider relationalAnnotationProvider)
{
Check.NotNull(singletonModelDependencies, nameof(singletonModelDependencies));
Check.NotNull(relationalAnnotationProvider, nameof(relationalAnnotationProvider));

RelationalModelDependencies = singletonModelDependencies;
RelationalAnnotationProvider = relationalAnnotationProvider;
}

/// <summary>
/// The relational model dependencies.
/// </summary>
public RelationalModelDependencies RelationalModelDependencies { get; [param: NotNull] init; }

/// <summary>
/// The relational annotation provider.
/// </summary>
public IRelationalAnnotationProvider RelationalAnnotationProvider { get; [param: NotNull] init; }
}
}
Loading

0 comments on commit 9c1117f

Please sign in to comment.