Skip to content

Commit

Permalink
Refactor MigrationsModelDiffer to use the database model.
Browse files Browse the repository at this point in the history
Change IMigrationsAnnotationProvider to use the database model and split a part to IRelationalAnnotationProvider
Add IRelationalModel
Add more shared objects validation

Part of #2266
  • Loading branch information
AndriySvyryd committed Mar 24, 2020
1 parent ac46335 commit 3eec55e
Show file tree
Hide file tree
Showing 101 changed files with 2,027 additions and 1,492 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Design;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
Expand Down Expand Up @@ -107,6 +108,7 @@ public static IServiceCollection AddDbContextDesignTimeServices(
.AddTransient(_ => context.GetService<IMigrationsModelDiffer>())
.AddTransient(_ => context.GetService<IMigrator>())
.AddTransient(_ => context.GetService<IRelationalTypeMappingSource>())
.AddTransient(_ => context.GetService<IModel>());
.AddTransient(_ => context.GetService<IModel>())
.AddTransient(_ => context.GetService<IConventionSetBuilder>());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,10 @@ public virtual void Generate(string builderName, IModel model, IndentedStringBui
ChangeDetector.SkipDetectChangesAnnotation,
CoreAnnotationNames.ChangeTrackingStrategy,
CoreAnnotationNames.OwnedTypes,
RelationalAnnotationNames.RelationalModel,
RelationalAnnotationNames.CheckConstraints,
RelationalAnnotationNames.Sequences,
RelationalAnnotationNames.DbFunctions,
RelationalAnnotationNames.Tables,
RelationalAnnotationNames.Views);
RelationalAnnotationNames.DbFunctions);

if (annotations.Count > 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,13 +241,12 @@ private IEnumerable<string> GetAnnotationNamespaces(IEnumerable<IAnnotatable> it
CoreAnnotationNames.ValueGeneratorFactory,
CoreAnnotationNames.DefiningQuery,
CoreAnnotationNames.QueryFilter,
RelationalAnnotationNames.RelationalModel,
RelationalAnnotationNames.CheckConstraints,
RelationalAnnotationNames.Sequences,
RelationalAnnotationNames.DbFunctions,
RelationalAnnotationNames.Tables,
RelationalAnnotationNames.TableMappings,
RelationalAnnotationNames.TableColumnMappings,
RelationalAnnotationNames.Views,
RelationalAnnotationNames.ViewMappings,
RelationalAnnotationNames.ViewColumnMappings,
RelationalAnnotationNames.ForeignKeyMappings,
Expand Down
9 changes: 5 additions & 4 deletions src/EFCore.Design/Migrations/Design/MigrationsScaffolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,11 @@ public virtual ScaffoldedMigration ScaffoldMigration(
}

var modelSnapshot = Dependencies.MigrationsAssembly.ModelSnapshot;
var lastModel = Dependencies.SnapshotModelProcessor.Process(modelSnapshot?.Model);
var upOperations = Dependencies.MigrationsModelDiffer.GetDifferences(lastModel, Dependencies.Model);
var lastModel = Dependencies.SnapshotModelProcessor.Process(modelSnapshot?.Model)?.GetRelationalModel();
var upOperations = Dependencies.MigrationsModelDiffer
.GetDifferences(lastModel, Dependencies.Model.GetRelationalModel());
var downOperations = upOperations.Count > 0
? Dependencies.MigrationsModelDiffer.GetDifferences(Dependencies.Model, lastModel)
? Dependencies.MigrationsModelDiffer.GetDifferences(Dependencies.Model.GetRelationalModel(), lastModel)
: new List<MigrationOperation>();
var migrationId = Dependencies.MigrationsIdGenerator.GenerateId(migrationName);
var modelSnapshotNamespace = GetNamespace(modelSnapshot?.GetType(), migrationNamespace);
Expand Down Expand Up @@ -245,7 +246,7 @@ public virtual MigrationFiles RemoveMigration(
model = migration.TargetModel;

if (!Dependencies.MigrationsModelDiffer.HasDifferences(
model, Dependencies.SnapshotModelProcessor.Process(modelSnapshot.Model)))
model.GetRelationalModel(), Dependencies.SnapshotModelProcessor.Process(modelSnapshot.Model).GetRelationalModel()))
{
var applied = false;
try
Expand Down
20 changes: 18 additions & 2 deletions src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
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 @@ -25,6 +26,7 @@ public class SnapshotModelProcessor : ISnapshotModelProcessor
{
private readonly IOperationReporter _operationReporter;
private readonly HashSet<string> _relationalNames;
private readonly IConventionSetBuilder _conventionSetBuilder;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -33,14 +35,16 @@ public class SnapshotModelProcessor : ISnapshotModelProcessor
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public SnapshotModelProcessor(
[NotNull] IOperationReporter operationReporter)
[NotNull] IOperationReporter operationReporter,
[NotNull] IConventionSetBuilder conventionSetBuilder)
{
_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;
}

/// <summary>
Expand Down Expand Up @@ -80,7 +84,19 @@ public virtual IModel Process(IModel model)

if (model is IConventionModel conventionModel)
{
model = new RelationalModelConvention().ProcessModelFinalized(conventionModel);
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);
}
}

return model is IMutableModel mutableModel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,9 @@ protected virtual void GenerateOnModelCreating(
RemoveAnnotation(ref annotations, CoreAnnotationNames.OwnedTypes);
RemoveAnnotation(ref annotations, ChangeDetector.SkipDetectChangesAnnotation);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.MaxIdentifierLength);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.RelationalModel);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.CheckConstraints);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.Sequences);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.Tables);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.Views);
RemoveAnnotation(ref annotations, ScaffoldingAnnotationNames.DatabaseName);
RemoveAnnotation(ref annotations, ScaffoldingAnnotationNames.EntityTypeErrors);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -521,9 +521,10 @@ public static string SetComment(
/// <returns>A value indicating whether the entity type is ignored by Migrations.</returns>
public static bool IsIgnoredByMigrations([NotNull] this IEntityType entityType)
{
if (entityType.BaseType != null)
if (entityType.BaseType != null
&& entityType.BaseType.IsIgnoredByMigrations())
{
return entityType.BaseType.IsIgnoredByMigrations();
return true;
}

if (entityType.GetTableName() != null)
Expand All @@ -532,7 +533,9 @@ public static bool IsIgnoredByMigrations([NotNull] this IEntityType entityType)
}

if (entityType.FindAnnotation(RelationalAnnotationNames.QueryableFunctionResultType) != null)
{
return true;
}

var viewDefinition = entityType.FindAnnotation(RelationalAnnotationNames.ViewDefinition);
if (viewDefinition?.Value != null)
Expand Down
62 changes: 15 additions & 47 deletions src/EFCore.Relational/Extensions/RelationalModelExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// 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 System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Utilities;
Expand Down Expand Up @@ -60,53 +62,19 @@ public static string SetDefaultSchema(
=> model.FindAnnotation(RelationalAnnotationNames.DefaultSchema)?.GetConfigurationSource();

/// <summary>
/// Returns all the tables mapped in the model.
/// Returns the database model.
/// </summary>
/// <param name="model"> The model to get the tables for. </param>
/// <returns> All the tables mapped in the model. </returns>
public static IEnumerable<ITable> GetTables([NotNull] this IModel model) =>
((IDictionary<(string, string), Table>)model[RelationalAnnotationNames.Tables])?.Values
?? Enumerable.Empty<ITable>();

/// <summary>
/// Gets the table with a given name. Returns <c>null</c> if no table with the given name is defined.
/// </summary>
/// <param name="model"> The model to get the table for. </param>
/// <param name="name"> The name of the table. </param>
/// <param name="schema"> The schema of the table. </param>
/// <returns> The table with a given name or <c>null</c> if no table with the given name is defined. </returns>
public static ITable FindTable([NotNull] this IModel model, [NotNull] string name, [CanBeNull] string schema)
/// <param name="model"> The model to get the database model for. </param>
/// <returns> The database model. </returns>
public static IRelationalModel GetRelationalModel([NotNull] this IModel model)
{
Table table = null;
return ((IDictionary<(string, string), Table>)model[RelationalAnnotationNames.Tables])
?.TryGetValue((name, schema), out table) == true
? table
: null;
}

/// <summary>
/// Returns all the views mapped in the model.
/// </summary>
/// <param name="model"> The model to get the views for. </param>
/// <returns> All the views mapped in the model. </returns>
public static IEnumerable<IView> GetViews([NotNull] this IModel model) =>
((IDictionary<(string, string), View>)model[RelationalAnnotationNames.Views])?.Values
?? Enumerable.Empty<IView>();
var databaseModel = (IRelationalModel)model[RelationalAnnotationNames.RelationalModel];
if (databaseModel == null)
{
throw new InvalidOperationException(RelationalStrings.DatabaseModelMissing);
}

/// <summary>
/// Gets the view with a given name. Returns <c>null</c> if no view with the given name is defined.
/// </summary>
/// <param name="model"> The model to get the view for. </param>
/// <param name="name"> The name of the view. </param>
/// <param name="schema"> The schema of the view. </param>
/// <returns> The view with a given name or <c>null</c> if no view with the given name is defined. </returns>
public static IView FindView([NotNull] this IModel model, [NotNull] string name, [CanBeNull] string schema)
{
View view = null;
return ((IDictionary<(string, string), View>)model[RelationalAnnotationNames.Views])
?.TryGetValue((name, schema), out view) == true
? view
: null;
return databaseModel;
}

/// <summary>
Expand Down Expand Up @@ -295,7 +263,7 @@ public static IConventionDbFunction FindDbFunction([NotNull] this IConventionMod
=> (IConventionDbFunction)((IModel)model).FindDbFunction(method);

/// <summary>
/// Finds a <see cref="IDbFunction" /> that is mapped to the method represented by the given <see cref="MethodInfo" />.
/// Finds a <see cref="IDbFunction" /> that is mapped to the method represented by the given name.
/// </summary>
/// <param name="model"> The model to find the function in. </param>
/// <param name="name"> The model name of the function. </param>
Expand All @@ -306,7 +274,7 @@ public static IDbFunction FindDbFunction([NotNull] this IModel model, [NotNull]
Check.NotNull(name, nameof(name)));

/// <summary>
/// Finds a <see cref="IMutableDbFunction" /> that is mapped to the method represented by the given <see cref="MethodInfo" />.
/// Finds a <see cref="IMutableDbFunction" /> that is mapped to the method represented by the given name.
/// </summary>
/// <param name="model"> The model to find the function in. </param>
/// <param name="name"> The model name of the function. </param>
Expand All @@ -315,7 +283,7 @@ public static IMutableDbFunction FindDbFunction([NotNull] this IMutableModel mod
=> (IMutableDbFunction)((IModel)model).FindDbFunction(name);

/// <summary>
/// Finds a <see cref="IConventionDbFunction" /> that is mapped to the method represented by the given <see cref="MethodInfo" />.
/// Finds a <see cref="IConventionDbFunction" /> that is mapped to the method represented by the given name.
/// </summary>
/// <param name="model"> The model to find the function in. </param>
/// <param name="name"> The model name of the function. </param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Diagnostics.Internal;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
Expand Down Expand Up @@ -59,6 +60,7 @@ public static readonly IDictionary<Type, ServiceCharacteristics> RelationalServi
{ typeof(IComparer<ModificationCommand>), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IMigrationsIdGenerator), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(ISqlGenerationHelper), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IRelationalAnnotationProvider), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IMigrationsAnnotationProvider), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IMigrationCommandExecutor), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IRelationalTypeMappingSource), new ServiceCharacteristics(ServiceLifetime.Singleton) },
Expand Down Expand Up @@ -130,6 +132,7 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
TryAdd<IMigrationsIdGenerator, MigrationsIdGenerator>();
TryAdd<IKeyValueIndexFactorySource, KeyValueIndexFactorySource>();
TryAdd<IModelCustomizer, RelationalModelCustomizer>();
TryAdd<IRelationalAnnotationProvider, RelationalAnnotationProvider>();
TryAdd<IMigrationsAnnotationProvider, MigrationsAnnotationProvider>();
TryAdd<IModelValidator, RelationalModelValidator>();
TryAdd<IMigrator, Migrator>();
Expand Down Expand Up @@ -175,6 +178,7 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
.AddDependencySingleton<RelationalTypeMappingSourceDependencies>()
.AddDependencySingleton<RelationalModelValidatorDependencies>()
.AddDependencySingleton<UpdateSqlGeneratorDependencies>()
.AddDependencySingleton<RelationalAnnotationProviderDependencies>()
.AddDependencySingleton<MigrationsAnnotationProviderDependencies>()
.AddDependencySingleton<ParameterNameGeneratorDependencies>()
.AddDependencySingleton<RelationalValueBufferFactoryDependencies>()
Expand Down
Loading

0 comments on commit 3eec55e

Please sign in to comment.