Skip to content

Commit b8a9d2e

Browse files
committed
Add runtime annotation support to model.
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
1 parent e138067 commit b8a9d2e

File tree

128 files changed

+1921
-932
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

128 files changed

+1921
-932
lines changed

src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
using Microsoft.EntityFrameworkCore.Infrastructure;
1111
using Microsoft.EntityFrameworkCore.Internal;
1212
using Microsoft.EntityFrameworkCore.Metadata;
13-
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
14-
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
1513
using Microsoft.EntityFrameworkCore.Metadata.Internal;
1614

1715
namespace Microsoft.EntityFrameworkCore.Migrations.Internal
@@ -26,7 +24,7 @@ public class SnapshotModelProcessor : ISnapshotModelProcessor
2624
{
2725
private readonly IOperationReporter _operationReporter;
2826
private readonly HashSet<string> _relationalNames;
29-
private readonly IConventionSetBuilder _conventionSetBuilder;
27+
private readonly IModelRuntimeInitializer _modelRuntimeInitializer;
3028

3129
/// <summary>
3230
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -36,15 +34,15 @@ public class SnapshotModelProcessor : ISnapshotModelProcessor
3634
/// </summary>
3735
public SnapshotModelProcessor(
3836
[NotNull] IOperationReporter operationReporter,
39-
[NotNull] IConventionSetBuilder conventionSetBuilder)
37+
[NotNull] IModelRuntimeInitializer modelRuntimeInitializer)
4038
{
4139
_operationReporter = operationReporter;
4240
_relationalNames = new HashSet<string>(
4341
typeof(RelationalAnnotationNames)
4442
.GetRuntimeFields()
4543
.Where(p => p.Name != nameof(RelationalAnnotationNames.Prefix))
4644
.Select(p => ((string)p.GetValue(null)).Substring(RelationalAnnotationNames.Prefix.Length - 1)));
47-
_conventionSetBuilder = conventionSetBuilder;
45+
_modelRuntimeInitializer = modelRuntimeInitializer;
4846
}
4947

5048
/// <summary>
@@ -82,27 +80,12 @@ public virtual IModel Process(IModel model)
8280
}
8381
}
8482

85-
if (model is IConventionModel conventionModel)
83+
if (model is IMutableModel mutableModel)
8684
{
87-
var conventionSet = _conventionSetBuilder.CreateConventionSet();
88-
89-
var typeMappingConvention = conventionSet.ModelFinalizingConventions.OfType<TypeMappingConvention>().FirstOrDefault();
90-
if (typeMappingConvention != null)
91-
{
92-
typeMappingConvention.ProcessModelFinalizing(conventionModel.Builder, null);
93-
}
94-
95-
var relationalModelConvention =
96-
conventionSet.ModelFinalizedConventions.OfType<RelationalModelConvention>().FirstOrDefault();
97-
if (relationalModelConvention != null)
98-
{
99-
model = relationalModelConvention.ProcessModelFinalized(conventionModel);
100-
}
85+
model = mutableModel.FinalizeModel();
10186
}
10287

103-
return model is IMutableModel mutableModel
104-
? mutableModel.FinalizeModel()
105-
: model;
88+
return _modelRuntimeInitializer.Initialize(model, validationLogger: null);
10689
}
10790

10891
private void ProcessCollection(IEnumerable<IAnnotatable> metadata, string version)

src/EFCore.Relational/Design/AnnotationCodeGenerator.cs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,9 @@ public class AnnotationCodeGenerator : IAnnotationCodeGenerator
2929
{
3030
private static readonly ISet<string> _ignoredRelationalAnnotations = new HashSet<string>
3131
{
32-
RelationalAnnotationNames.RelationalModel,
3332
RelationalAnnotationNames.CheckConstraints,
3433
RelationalAnnotationNames.Sequences,
3534
RelationalAnnotationNames.DbFunctions,
36-
RelationalAnnotationNames.DefaultMappings,
37-
RelationalAnnotationNames.DefaultColumnMappings,
38-
RelationalAnnotationNames.TableMappings,
39-
RelationalAnnotationNames.TableColumnMappings,
40-
RelationalAnnotationNames.ViewMappings,
41-
RelationalAnnotationNames.ViewColumnMappings,
42-
RelationalAnnotationNames.FunctionMappings,
43-
RelationalAnnotationNames.FunctionColumnMappings,
44-
RelationalAnnotationNames.SqlQueryMappings,
45-
RelationalAnnotationNames.SqlQueryColumnMappings,
46-
RelationalAnnotationNames.ForeignKeyMappings,
47-
RelationalAnnotationNames.TableIndexMappings,
48-
RelationalAnnotationNames.UniqueConstraintMappings,
4935
RelationalAnnotationNames.RelationalOverrides
5036
};
5137

src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ public static void SetSchema([NotNull] this IMutableEntityType entityType, [CanB
247247
/// <param name="entityType"> The entity type to get the table mappings for. </param>
248248
/// <returns> The tables to which the entity type is mapped. </returns>
249249
public static IEnumerable<ITableMappingBase> GetDefaultMappings([NotNull] this IEntityType entityType)
250-
=> (IEnumerable<ITableMappingBase>?)entityType[RelationalAnnotationNames.DefaultMappings]
250+
=> (IEnumerable<ITableMappingBase>?)entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.DefaultMappings)
251251
?? Array.Empty<ITableMappingBase>();
252252

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

262262
/// <summary>
@@ -419,7 +419,7 @@ public static void SetViewSchema([NotNull] this IMutableEntityType entityType, [
419419
/// <param name="entityType"> The entity type to get the view mappings for. </param>
420420
/// <returns> The views to which the entity type is mapped. </returns>
421421
public static IEnumerable<IViewMapping> GetViewMappings([NotNull] this IEntityType entityType)
422-
=> (IEnumerable<IViewMapping>?)entityType[RelationalAnnotationNames.ViewMappings]
422+
=> (IEnumerable<IViewMapping>?)entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.ViewMappings)
423423
?? Array.Empty<IViewMapping>();
424424

425425
/// <summary>
@@ -493,7 +493,7 @@ public static void SetSqlQuery([NotNull] this IMutableEntityType entityType, [Ca
493493
/// <param name="entityType"> The entity type to get the function mappings for. </param>
494494
/// <returns> The functions to which the entity type is mapped. </returns>
495495
public static IEnumerable<ISqlQueryMapping> GetSqlQueryMappings([NotNull] this IEntityType entityType)
496-
=> (IEnumerable<ISqlQueryMapping>?)entityType[RelationalAnnotationNames.SqlQueryMappings]
496+
=> (IEnumerable<ISqlQueryMapping>?)entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.SqlQueryMappings)
497497
?? Array.Empty<ISqlQueryMapping>();
498498

499499
/// <summary>
@@ -558,7 +558,7 @@ public static void SetFunctionName([NotNull] this IMutableEntityType entityType,
558558
/// <param name="entityType"> The entity type to get the function mappings for. </param>
559559
/// <returns> The functions to which the entity type is mapped. </returns>
560560
public static IEnumerable<IFunctionMapping> GetFunctionMappings([NotNull] this IEntityType entityType)
561-
=> (IEnumerable<IFunctionMapping>?)entityType[RelationalAnnotationNames.FunctionMappings]
561+
=> (IEnumerable<IFunctionMapping>?)entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.FunctionMappings)
562562
?? Array.Empty<IFunctionMapping>();
563563

564564
/// <summary>

src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public static void SetConstraintName([NotNull] this IMutableForeignKey foreignKe
190190
/// <param name="foreignKey"> The foreign key. </param>
191191
/// <returns> The foreign key constraints to which the foreign key is mapped. </returns>
192192
public static IEnumerable<IForeignKeyConstraint> GetMappedConstraints([NotNull] this IForeignKey foreignKey)
193-
=> (IEnumerable<IForeignKeyConstraint>?)foreignKey[RelationalAnnotationNames.ForeignKeyMappings]
193+
=> (IEnumerable<IForeignKeyConstraint>?)foreignKey.FindRuntimeAnnotationValue(RelationalAnnotationNames.ForeignKeyMappings)
194194
?? Enumerable.Empty<IForeignKeyConstraint>();
195195

196196
/// <summary>

src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ public static void SetFilter([NotNull] this IMutableIndex index, [CanBeNull] str
270270
/// <param name="index"> The index. </param>
271271
/// <returns> The table indexes to which the index is mapped. </returns>
272272
public static IEnumerable<ITableIndex> GetMappedTableIndexes([NotNull] this IIndex index)
273-
=> (IEnumerable<ITableIndex>?)index[RelationalAnnotationNames.TableIndexMappings]
273+
=> (IEnumerable<ITableIndex>?)index.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableIndexMappings)
274274
?? Enumerable.Empty<ITableIndex>();
275275

276276
/// <summary>

src/EFCore.Relational/Extensions/RelationalKeyExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ public static void SetName([NotNull] this IMutableKey key, [CanBeNull] string? n
184184
/// <param name="key"> The key. </param>
185185
/// <returns> The unique constraints to which the key is mapped. </returns>
186186
public static IEnumerable<IUniqueConstraint> GetMappedConstraints([NotNull] this IKey key)
187-
=> (IEnumerable<IUniqueConstraint>?)key[RelationalAnnotationNames.UniqueConstraintMappings]
187+
=> (IEnumerable<IUniqueConstraint>?)key.FindRuntimeAnnotationValue(RelationalAnnotationNames.UniqueConstraintMappings)
188188
?? Enumerable.Empty<IUniqueConstraint>();
189189

190190
/// <summary>

src/EFCore.Relational/Extensions/RelationalModelExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,10 @@ public static void SetDefaultSchema([NotNull] this IMutableModel model, [CanBeNu
134134
/// <returns> The database model. </returns>
135135
public static IRelationalModel GetRelationalModel([NotNull] this IModel model)
136136
{
137-
var databaseModel = (IRelationalModel?)model[RelationalAnnotationNames.RelationalModel];
137+
var databaseModel = (IRelationalModel?)model.FindRuntimeAnnotationValue(RelationalAnnotationNames.RelationalModel);
138138
if (databaseModel == null)
139139
{
140-
throw new InvalidOperationException(RelationalStrings.DatabaseModelMissing);
140+
throw new InvalidOperationException(CoreStrings.ModelNotFinalized(nameof(GetRelationalModel)));
141141
}
142142

143143
return databaseModel;

src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ public static void SetColumnType([NotNull] this IMutableProperty property, [CanB
362362
/// <param name="property"> The property. </param>
363363
/// <returns> The default columns to which the property would be mapped. </returns>
364364
public static IEnumerable<IColumnMappingBase> GetDefaultColumnMappings([NotNull] this IProperty property)
365-
=> (IEnumerable<IColumnMappingBase>?)property[RelationalAnnotationNames.DefaultColumnMappings]
365+
=> (IEnumerable<IColumnMappingBase>?)property.FindRuntimeAnnotationValue(RelationalAnnotationNames.DefaultColumnMappings)
366366
?? Enumerable.Empty<IColumnMappingBase>();
367367

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

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

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

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

404404
/// <summary>

src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
134134
TryAdd<IMigrationsIdGenerator, MigrationsIdGenerator>();
135135
TryAdd<IKeyValueIndexFactorySource, KeyValueIndexFactorySource>();
136136
TryAdd<IModelCustomizer, RelationalModelCustomizer>();
137+
TryAdd<IModelRuntimeInitializer, RelationalModelRuntimeInitializer>();
137138
TryAdd<IRelationalAnnotationProvider, RelationalAnnotationProvider>();
138139
TryAdd<IMigrationsAnnotationProvider, MigrationsAnnotationProvider>();
139140
TryAdd<IModelValidator, RelationalModelValidator>();
@@ -198,6 +199,8 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
198199
.AddDependencySingleton<RelationalEvaluatableExpressionFilterDependencies>()
199200
.AddDependencySingleton<RelationalQueryTranslationPreprocessorDependencies>()
200201
.AddDependencySingleton<RelationalParameterBasedSqlProcessorDependencies>()
202+
.AddDependencySingleton<RelationalModelDependencies>()
203+
.AddDependencySingleton<RelationalModelRuntimeInitializerDependencies>()
201204
.AddDependencyScoped<MigrationsSqlGeneratorDependencies>()
202205
.AddDependencyScoped<RelationalConventionSetBuilderDependencies>()
203206
.AddDependencyScoped<ModificationCommandBatchFactoryDependencies>()
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.Extensions.DependencyInjection;
5+
6+
#nullable enable
7+
8+
namespace Microsoft.EntityFrameworkCore.Infrastructure
9+
{
10+
/// <summary>
11+
/// <para>
12+
/// The relational model service dependencies.
13+
/// </para>
14+
/// <para>
15+
/// This type is typically used by database providers (and other extensions). It is generally
16+
/// not used in application code.
17+
/// </para>
18+
/// <para>
19+
/// Do not construct instances of this class directly from either provider or application code as the
20+
/// constructor signature may change as new dependencies are added. Instead, use this type in
21+
/// your constructor so that an instance will be created and injected automatically by the
22+
/// dependency injection container. To create an instance with some dependent services replaced,
23+
/// first resolve the object from the dependency injection container, then replace selected
24+
/// services using the 'With...' methods. Do not call the constructor at any point in this process.
25+
/// </para>
26+
/// <para>
27+
/// The service lifetime is <see cref="ServiceLifetime.Singleton" />. This means that each
28+
/// <see cref="DbContext" /> instance will use its own instance of this service.
29+
/// The implementation may depend on other services registered with any lifetime.
30+
/// The implementation does not need to be thread-safe.
31+
/// </para>
32+
/// </summary>
33+
public sealed record RelationalModelDependencies
34+
{
35+
/// <summary>
36+
/// <para>
37+
/// Creates the relational model service dependencies.
38+
/// </para>
39+
/// <para>
40+
/// Do not call this constructor directly from either provider or application code as it may change
41+
/// as new dependencies are added. Instead, use this type in your constructor so that an instance
42+
/// will be created and injected automatically by the dependency injection container. To create
43+
/// an instance with some dependent services replaced, first resolve the object from the dependency
44+
/// injection container, then replace selected services using the 'With...' methods. Do not call
45+
/// the constructor at any point in this process.
46+
/// </para>
47+
/// <para>
48+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
49+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
50+
/// any release. You should only use it directly in your code with extreme caution and knowing that
51+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
52+
/// </para>
53+
/// </summary>
54+
[EntityFrameworkInternal]
55+
public RelationalModelDependencies()
56+
{
57+
}
58+
}
59+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Runtime.CompilerServices;
6+
using JetBrains.Annotations;
7+
using Microsoft.EntityFrameworkCore.Diagnostics;
8+
using Microsoft.EntityFrameworkCore.Metadata;
9+
10+
#nullable enable
11+
12+
namespace Microsoft.EntityFrameworkCore.Infrastructure
13+
{
14+
/// <summary>
15+
/// Relational-specific extension methods for <see cref="IModel" />.
16+
/// </summary>
17+
public static class RelationalModelExtensions
18+
{
19+
/// <summary>
20+
/// Returns the relational service dependencies.
21+
/// </summary>
22+
/// <param name="model"> The model. </param>
23+
/// <param name="methodName"> The name of the calling method. </param>
24+
/// <returns> The relational service dependencies. </returns>
25+
public static RelationalModelDependencies GetRelationalDependencies(
26+
[NotNull] this IModel model, [CallerMemberName][CanBeNull] string methodName = "")
27+
=> (RelationalModelDependencies?)model
28+
.FindRuntimeAnnotation(RelationalAnnotationNames.ModelDependencies)?.Value
29+
?? throw new InvalidOperationException(CoreStrings.ModelNotFinalized(methodName));
30+
}
31+
}

0 commit comments

Comments
 (0)