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 21, 2021
1 parent f54b9dc commit ac990c0
Show file tree
Hide file tree
Showing 128 changed files with 1,889 additions and 926 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 @@ -247,7 +247,7 @@ public static void SetSchema([NotNull] this IMutableEntityType entityType, [CanB
/// <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 @@ -256,7 +256,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 @@ -419,7 +419,7 @@ public static void SetViewSchema([NotNull] this IMutableEntityType entityType, [
/// <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 @@ -493,7 +493,7 @@ public static void SetSqlQuery([NotNull] this IMutableEntityType entityType, [Ca
/// <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 @@ -558,7 +558,7 @@ public static void SetFunctionName([NotNull] this IMutableEntityType entityType,
/// <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 @@ -190,7 +190,7 @@ public static void SetConstraintName([NotNull] this IMutableForeignKey foreignKe
/// <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 @@ -270,7 +270,7 @@ public static void SetFilter([NotNull] this IMutableIndex index, [CanBeNull] str
/// <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 @@ -184,7 +184,7 @@ public static void SetName([NotNull] this IMutableKey key, [CanBeNull] string? n
/// <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 @@ -362,7 +362,7 @@ public static void SetColumnType([NotNull] this IMutableProperty property, [CanB
/// <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 @@ -371,7 +371,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 @@ -380,7 +380,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 @@ -389,7 +389,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 @@ -398,7 +398,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 ac990c0

Please sign in to comment.