diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelCustomizer.cs b/src/EFCore.Relational/Infrastructure/RelationalModelCustomizer.cs index 725bc7b67fc..1767977204b 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelCustomizer.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelCustomizer.cs @@ -1,26 +1,14 @@ // 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.Linq; -using System.Reflection; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Internal; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Utilities; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.EntityFrameworkCore.Infrastructure { /// /// - /// Builds the model for a given context. This default implementation builds the model by calling - /// on the context. - /// - /// - /// Also, entity types found as properties on the context are mapped - /// to tables named for the DbSet property names, and public static methods on the context marked with - /// are mapped to database functions. + /// Builds the model for a given context. /// /// /// This type is typically used by database providers (and other extensions). It is generally @@ -42,80 +30,5 @@ public RelationalModelCustomizer([NotNull] ModelCustomizerDependencies dependenc : base(dependencies) { } - - /// - /// - /// Performs additional configuration of the model in addition to what is discovered by convention. This implementation - /// builds the model for a given context by calling - /// on the context. - /// - /// - /// Also, entity types found as properties on the context are mapped - /// to tables named for the DbSet property names, and public static methods on the context marked with - /// are mapped to database functions. - /// - /// - /// - /// The builder being used to construct the model. - /// - /// - /// The context instance that the model is being created for. - /// - public override void Customize(ModelBuilder modelBuilder, DbContext context) - { - FindDbFunctions(modelBuilder, context); - - base.Customize(modelBuilder, context); - } - - /// - /// Adds to the model function mappings found as public static methods on the context marked with - /// the . - /// - /// The being used to build the model. - /// The context to find function methods on. - protected virtual void FindDbFunctions([NotNull] ModelBuilder modelBuilder, [NotNull] DbContext context) - { - Check.NotNull(modelBuilder, nameof(modelBuilder)); - Check.NotNull(context, nameof(context)); - - var contextType = context.GetType(); - - while (contextType != typeof(DbContext)) - { - var functions = contextType.GetMethods( - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance - | BindingFlags.Static | BindingFlags.DeclaredOnly) - .Where(mi => mi.GetCustomAttributes(typeof(DbFunctionAttribute)).Any()); - - foreach (var function in functions) - { - modelBuilder.HasDbFunction(function); - } - - contextType = contextType.BaseType; - } - } - - /// - /// Adds the entity types found in properties on the context to the model. - /// - /// The being used to build the model. - /// The context type to find properties on. - protected override void FindSets(ModelBuilder modelBuilder, Type contextType) - { - base.FindSets(modelBuilder, contextType); - - var sets = Dependencies.SetFinder.CreateClrTypeDbSetMapping(contextType); - - foreach (var entityType in modelBuilder.Model.GetEntityTypes().Cast()) - { - if (entityType.BaseType == null - && sets.ContainsKey(entityType.ClrType)) - { - entityType.Builder.ToTable(sets[entityType.ClrType].Name); - } - } - } } } diff --git a/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs b/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs index fe311c5fe3c..f0b7ddd88b2 100644 --- a/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs +++ b/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs @@ -70,13 +70,21 @@ public override ConventionSet CreateConventionSet() conventionSet.PropertyAddedConventions.Add(relationalColumnAttributeConvention); - var storeGenerationConvention = new StoreGenerationConvention(Dependencies, RelationalDependencies); + var tableNameFromDbSetConvention = new TableNameFromDbSetConvention(Dependencies, RelationalDependencies); conventionSet.EntityTypeAddedConventions.Add(new RelationalTableAttributeConvention(Dependencies, RelationalDependencies)); - conventionSet.EntityTypeBaseTypeChangedConventions.Add(new TableNameFromDbSetConvention(Dependencies, RelationalDependencies)); + conventionSet.EntityTypeAddedConventions.Add(tableNameFromDbSetConvention); + + conventionSet.EntityTypeBaseTypeChangedConventions.Add(tableNameFromDbSetConvention); + conventionSet.PropertyFieldChangedConventions.Add(relationalColumnAttributeConvention); + + var storeGenerationConvention = new StoreGenerationConvention(Dependencies, RelationalDependencies); conventionSet.PropertyAnnotationChangedConventions.Add(storeGenerationConvention); conventionSet.PropertyAnnotationChangedConventions.Add((RelationalValueGenerationConvention)valueGenerationConvention); + var dbFunctionAttributeConvention = new RelationalDbFunctionAttributeConvention(Dependencies, RelationalDependencies); + conventionSet.ModelInitializedConventions.Add(dbFunctionAttributeConvention); + var sharedTableConvention = new SharedTableConvention(Dependencies, RelationalDependencies); ConventionSet.AddBefore( conventionSet.ModelFinalizedConventions, @@ -87,7 +95,7 @@ public override ConventionSet CreateConventionSet() sharedTableConvention, typeof(ValidatingConvention)); - conventionSet.ModelAnnotationChangedConventions.Add(new RelationalDbFunctionConvention(Dependencies, RelationalDependencies)); + conventionSet.ModelAnnotationChangedConventions.Add(dbFunctionAttributeConvention); return conventionSet; } diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalDbFunctionConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalDbFunctionAttributeConvention.cs similarity index 70% rename from src/EFCore.Relational/Metadata/Conventions/RelationalDbFunctionConvention.cs rename to src/EFCore.Relational/Metadata/Conventions/RelationalDbFunctionAttributeConvention.cs index e0acd9874a5..8623ebb17de 100644 --- a/src/EFCore.Relational/Metadata/Conventions/RelationalDbFunctionConvention.cs +++ b/src/EFCore.Relational/Metadata/Conventions/RelationalDbFunctionAttributeConvention.cs @@ -12,17 +12,17 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions { /// - /// A convention that configures the name and schema for a based on the applied + /// A convention that configures model function mappings based on public static methods on the context marked with /// . /// - public class RelationalDbFunctionConvention : IModelAnnotationChangedConvention + public class RelationalDbFunctionAttributeConvention : IModelInitializedConvention, IModelAnnotationChangedConvention { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// Parameter object containing dependencies for this convention. /// Parameter object containing relational dependencies for this convention. - public RelationalDbFunctionConvention( + public RelationalDbFunctionAttributeConvention( [NotNull] ProviderConventionSetBuilderDependencies dependencies, [NotNull] RelationalConventionSetBuilderDependencies relationalDependencies) { @@ -34,6 +34,31 @@ public RelationalDbFunctionConvention( /// protected virtual ProviderConventionSetBuilderDependencies Dependencies { get; } + /// + /// Called after a model is initialized. + /// + /// The builder for the model. + /// Additional information associated with convention execution. + public virtual void ProcessModelInitialized(IConventionModelBuilder modelBuilder, IConventionContext context) + { + var contextType = Dependencies.ContextType; + while (contextType != null + && contextType != typeof(DbContext)) + { + var functions = contextType.GetMethods( + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance + | BindingFlags.Static | BindingFlags.DeclaredOnly) + .Where(mi => mi.IsDefined(typeof(DbFunctionAttribute))); + + foreach (var function in functions) + { + modelBuilder.HasDbFunction(function); + } + + contextType = contextType.BaseType; + } + } + /// /// Called after an annotation is changed on an model. /// diff --git a/src/EFCore.Relational/Metadata/Conventions/TableNameFromDbSetConvention.cs b/src/EFCore.Relational/Metadata/Conventions/TableNameFromDbSetConvention.cs index ad82d21dc22..bae5c258aae 100644 --- a/src/EFCore.Relational/Metadata/Conventions/TableNameFromDbSetConvention.cs +++ b/src/EFCore.Relational/Metadata/Conventions/TableNameFromDbSetConvention.cs @@ -13,7 +13,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions /// /// A convention that configures the table name based on the property name. /// - public class TableNameFromDbSetConvention : IEntityTypeBaseTypeChangedConvention + public class TableNameFromDbSetConvention : IEntityTypeAddedConvention, IEntityTypeBaseTypeChangedConvention { private readonly IDictionary _sets; @@ -60,11 +60,30 @@ public virtual void ProcessEntityTypeBaseTypeChanged( } else if (oldBaseType != null && newBaseType == null + && entityType.ClrType != null && _sets.ContainsKey(entityType.ClrType)) { entityTypeBuilder.ToTable(_sets[entityType.ClrType].Name); } } } + + /// + /// Called after an entity type is added to the model. + /// + /// The builder for the entity type. + /// Additional information associated with convention execution. + public virtual void ProcessEntityTypeAdded( + IConventionEntityTypeBuilder entityTypeBuilder, + IConventionContext context) + { + var entityType = entityTypeBuilder.Metadata; + if (entityType.BaseType == null + && entityType.ClrType != null + && _sets.ContainsKey(entityType.ClrType)) + { + entityTypeBuilder.ToTable(_sets[entityType.ClrType].Name); + } + } } } diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerConventionSetBuilder.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerConventionSetBuilder.cs index 3ab995d4615..4774a98ea4a 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerConventionSetBuilder.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerConventionSetBuilder.cs @@ -94,7 +94,7 @@ public override ConventionSet CreateConventionSet() ReplaceConvention( conventionSet.ModelAnnotationChangedConventions, - (RelationalDbFunctionConvention)new SqlServerDbFunctionConvention(Dependencies, RelationalDependencies)); + (RelationalDbFunctionAttributeConvention)new SqlServerDbFunctionAttributeConvention(Dependencies, RelationalDependencies)); ReplaceConvention(conventionSet.ModelFinalizedConventions, storeGenerationConvention); diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerDbFunctionConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerDbFunctionAttributeConvention.cs similarity index 82% rename from src/EFCore.SqlServer/Metadata/Conventions/SqlServerDbFunctionConvention.cs rename to src/EFCore.SqlServer/Metadata/Conventions/SqlServerDbFunctionAttributeConvention.cs index 73a0d2688ce..dba56194f0f 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerDbFunctionConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerDbFunctionAttributeConvention.cs @@ -10,17 +10,17 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions { /// - /// A convention that configures the name and schema for a based on the applied - /// and the default schema as 'dbo'. + /// A convention that configures model function mappings based on public static methods on the context marked with + /// and sets the default schema to 'dbo'. /// - public class SqlServerDbFunctionConvention : RelationalDbFunctionConvention + public class SqlServerDbFunctionAttributeConvention : RelationalDbFunctionAttributeConvention { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// Parameter object containing dependencies for this convention. /// Parameter object containing relational dependencies for this convention. - public SqlServerDbFunctionConvention( + public SqlServerDbFunctionAttributeConvention( [NotNull] ProviderConventionSetBuilderDependencies dependencies, [NotNull] RelationalConventionSetBuilderDependencies relationalDependencies) : base(dependencies, relationalDependencies) diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerIndexConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerIndexConvention.cs index 2123a0a1edc..b2707ec6de6 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerIndexConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerIndexConvention.cs @@ -27,7 +27,7 @@ public class SqlServerIndexConvention : private readonly ISqlGenerationHelper _sqlGenerationHelper; /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// Parameter object containing dependencies for this convention. /// Parameter object containing relational dependencies for this convention. diff --git a/src/EFCore/Infrastructure/ModelCustomizer.cs b/src/EFCore/Infrastructure/ModelCustomizer.cs index 4e16d35ab96..736b6ab4913 100644 --- a/src/EFCore/Infrastructure/ModelCustomizer.cs +++ b/src/EFCore/Infrastructure/ModelCustomizer.cs @@ -1,7 +1,6 @@ // 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 JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Utilities; using Microsoft.Extensions.DependencyInjection; @@ -54,29 +53,7 @@ public ModelCustomizer([NotNull] ModelCustomizerDependencies dependencies) /// public virtual void Customize(ModelBuilder modelBuilder, DbContext context) { - FindSets(modelBuilder, context.GetType()); - context.OnModelCreating(modelBuilder); } - - /// - /// Adds the entity types found in properties on the context to the model. - /// - /// The being used to build the model. - /// The context type to find properties on. - protected virtual void FindSets([NotNull] ModelBuilder modelBuilder, [NotNull] Type contextType) - { - foreach (var setInfo in Dependencies.SetFinder.FindSets(contextType)) - { - if (setInfo.IsKeyless) - { - modelBuilder.Entity(setInfo.ClrType).HasNoKey(); - } - else - { - modelBuilder.Entity(setInfo.ClrType); - } - } - } } } diff --git a/src/EFCore/Metadata/Conventions/DbSetFindingConvention.cs b/src/EFCore/Metadata/Conventions/DbSetFindingConvention.cs new file mode 100644 index 00000000000..4e3f79ee57b --- /dev/null +++ b/src/EFCore/Metadata/Conventions/DbSetFindingConvention.cs @@ -0,0 +1,50 @@ +// 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.Builders; +using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; + +namespace Microsoft.EntityFrameworkCore.Metadata.Conventions +{ + /// + /// A convention that adds entity types based on the properties defined on the + /// derived class. + /// + public class DbSetFindingConvention : IModelInitializedConvention + { + /// + /// Creates a new instance of . + /// + /// Parameter object containing dependencies for this convention. + public DbSetFindingConvention([NotNull] ProviderConventionSetBuilderDependencies dependencies) + { + Dependencies = dependencies; + } + + /// + /// Parameter object containing service dependencies. + /// + protected virtual ProviderConventionSetBuilderDependencies Dependencies { get; } + + /// + /// Called after a model is initialized. + /// + /// The builder for the model. + /// Additional information associated with convention execution. + public virtual void ProcessModelInitialized(IConventionModelBuilder modelBuilder, IConventionContext context) + { + foreach (var setInfo in Dependencies.SetFinder.FindSets(Dependencies.ContextType)) + { + if (setInfo.IsKeyless) + { + modelBuilder.Entity(setInfo.ClrType, fromDataAnnotation: true).HasNoKey(fromDataAnnotation: true); + } + else + { + modelBuilder.Entity(setInfo.ClrType, fromDataAnnotation: true); + } + } + } + } +} diff --git a/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs b/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs index 9da10e28d7a..adfca84ccf4 100644 --- a/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs +++ b/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs @@ -160,6 +160,8 @@ public virtual ConventionSet CreateConventionSet() conventionSet.ForeignKeyOwnershipChangedConventions.Add(keyDiscoveryConvention); conventionSet.ForeignKeyOwnershipChangedConventions.Add(relationshipDiscoveryConvention); + conventionSet.ModelInitializedConventions.Add(new DbSetFindingConvention(Dependencies)); + conventionSet.ModelFinalizedConventions.Add(new ModelCleanupConvention(Dependencies)); conventionSet.ModelFinalizedConventions.Add(keyAttributeConvention); conventionSet.ModelFinalizedConventions.Add(foreignKeyAttributeConvention); diff --git a/test/EFCore.Cosmos.Tests/TestUtilities/CosmosTestHelpers.cs b/test/EFCore.Cosmos.Tests/TestUtilities/CosmosTestHelpers.cs index dc4dd7e57df..37480ba4279 100644 --- a/test/EFCore.Cosmos.Tests/TestUtilities/CosmosTestHelpers.cs +++ b/test/EFCore.Cosmos.Tests/TestUtilities/CosmosTestHelpers.cs @@ -20,29 +20,12 @@ protected CosmosTestHelpers() public static CosmosTestHelpers Instance { get; } = new CosmosTestHelpers(); public override IServiceCollection AddProviderServices(IServiceCollection services) - { - return services.AddEntityFrameworkCosmos(); - } - - public override IModelValidator CreateModelValidator() - { - var typeMappingSource = new CosmosTypeMappingSource( - TestServiceFactory.Instance.Create()); - - return new ModelValidator( - new ModelValidatorDependencies( - typeMappingSource, - new MemberClassifier( - typeMappingSource, - TestServiceFactory.Instance.Create()))); - } + => services.AddEntityFrameworkCosmos(); protected override void UseProviderOptions(DbContextOptionsBuilder optionsBuilder) - { - optionsBuilder.UseCosmos( + => optionsBuilder.UseCosmos( TestEnvironment.DefaultConnection, TestEnvironment.AuthToken, "UnitTests"); - } } } diff --git a/test/EFCore.InMemory.FunctionalTests/TestUtilities/InMemoryTestHelpers.cs b/test/EFCore.InMemory.FunctionalTests/TestUtilities/InMemoryTestHelpers.cs index 87b199fda0a..3eb205f852c 100644 --- a/test/EFCore.InMemory.FunctionalTests/TestUtilities/InMemoryTestHelpers.cs +++ b/test/EFCore.InMemory.FunctionalTests/TestUtilities/InMemoryTestHelpers.cs @@ -26,19 +26,6 @@ public override IServiceCollection AddProviderServices(IServiceCollection servic protected override void UseProviderOptions(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseInMemoryDatabase(nameof(InMemoryTestHelpers)); - public override IModelValidator CreateModelValidator() - { - var typeMappingSource = new InMemoryTypeMappingSource( - TestServiceFactory.Instance.Create()); - - return new ModelValidator( - new ModelValidatorDependencies( - typeMappingSource, - new MemberClassifier( - typeMappingSource, - TestServiceFactory.Instance.Create()))); - } - public override LoggingDefinitions LoggingDefinitions { get; } = new InMemoryLoggingDefinitions(); } } diff --git a/test/EFCore.Relational.Tests/Metadata/Conventions/Internal/RelationalPropertyMappingValidationConventionTest.cs b/test/EFCore.Relational.Tests/Metadata/Conventions/Internal/RelationalPropertyMappingValidationConventionTest.cs index b7fd446f0ba..57cdaa5efab 100644 --- a/test/EFCore.Relational.Tests/Metadata/Conventions/Internal/RelationalPropertyMappingValidationConventionTest.cs +++ b/test/EFCore.Relational.Tests/Metadata/Conventions/Internal/RelationalPropertyMappingValidationConventionTest.cs @@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.TestUtilities; using Xunit; @@ -16,7 +17,7 @@ public class RelationalPropertyMappingValidationConventionTest : PropertyMapping [Fact] public void Throws_when_added_property_is_not_mapped_to_store() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); var entityTypeBuilder = modelBuilder.Entity(typeof(NonPrimitiveAsPropertyEntity), ConfigurationSource.Convention); entityTypeBuilder.Property(typeof(Tuple), "LongProperty", ConfigurationSource.Explicit); entityTypeBuilder.Ignore(nameof(NonPrimitiveAsPropertyEntity.Property), ConfigurationSource.Explicit); @@ -30,7 +31,7 @@ public void Throws_when_added_property_is_not_mapped_to_store() [Fact] public void Throws_when_added_property_is_not_mapped_to_store_even_if_configured_to_use_column_type() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); var entityTypeBuilder = modelBuilder.Entity(typeof(NonPrimitiveNonNavigationAsPropertyEntity), ConfigurationSource.Convention); entityTypeBuilder.Property(typeof(Tuple), "LongProperty", ConfigurationSource.Explicit) .HasColumnType("some_int_mapping"); @@ -43,5 +44,20 @@ public void Throws_when_added_property_is_not_mapped_to_store_even_if_configured } protected override TestHelpers TestHelpers => RelationalTestHelpers.Instance; + + protected override IModelValidator CreateModelValidator() + { + var typeMappingSource = new TestRelationalTypeMappingSource( + TestServiceFactory.Instance.Create(), + TestServiceFactory.Instance.Create()); + + return new RelationalModelValidator( + new ModelValidatorDependencies( + typeMappingSource, + new MemberClassifier( + typeMappingSource, + TestServiceFactory.Instance.Create())), + new RelationalModelValidatorDependencies(typeMappingSource)); + } } } diff --git a/test/EFCore.Relational.Tests/Metadata/DbFunctionMetadataTests.cs b/test/EFCore.Relational.Tests/Metadata/DbFunctionMetadataTests.cs index 76b66f18199..bcc730bce33 100644 --- a/test/EFCore.Relational.Tests/Metadata/DbFunctionMetadataTests.cs +++ b/test/EFCore.Relational.Tests/Metadata/DbFunctionMetadataTests.cs @@ -228,11 +228,8 @@ var dup2methodInfo [Fact] public virtual void Finds_dbFunctions_on_dbContext() { - var modelBuilder = GetModelBuilder(); - - var customizer = new RelationalModelCustomizer(new ModelCustomizerDependencies(new DbSetFinder())); - - customizer.Customize(modelBuilder, new MyDerivedContext()); + var context = new MyDerivedContext(); + var modelBuilder = GetModelBuilder(context); foreach (var function in MyBaseContext.FunctionNames) { @@ -488,12 +485,15 @@ public virtual void Set_empty_function_name_throws() expectedMessage, Assert.Throws(() => modelBuilder.HasDbFunction(MethodAmi).HasName("")).Message); } - private ModelBuilder GetModelBuilder() + private ModelBuilder GetModelBuilder(DbContext dbContext = null) { var conventionSet = new ConventionSet(); - conventionSet.ModelAnnotationChangedConventions.Add( - new RelationalDbFunctionConvention(CreateDependencies(), CreateRelationalDependencies())); + var dbFunctionAttributeConvention =new RelationalDbFunctionAttributeConvention( + CreateDependencies().With(new CurrentDbContext (dbContext ?? new DbContext(new DbContextOptions()))), + CreateRelationalDependencies()); + conventionSet.ModelInitializedConventions.Add(dbFunctionAttributeConvention); + conventionSet.ModelAnnotationChangedConventions.Add(dbFunctionAttributeConvention); return new ModelBuilder(conventionSet); } diff --git a/test/EFCore.Relational.Tests/TestUtilities/RelationalTestHelpers.cs b/test/EFCore.Relational.Tests/TestUtilities/RelationalTestHelpers.cs index 62c2610959a..bd222a842cf 100644 --- a/test/EFCore.Relational.Tests/TestUtilities/RelationalTestHelpers.cs +++ b/test/EFCore.Relational.Tests/TestUtilities/RelationalTestHelpers.cs @@ -3,9 +3,6 @@ using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Internal; -using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.TestUtilities.FakeProvider; using Microsoft.Extensions.DependencyInjection; @@ -31,21 +28,6 @@ protected override void UseProviderOptions(DbContextOptionsBuilder optionsBuilde extension.WithConnection(new FakeDbConnection("Database=Fake"))); } - public override IModelValidator CreateModelValidator() - { - var typeMappingSource = new TestRelationalTypeMappingSource( - TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create()); - - return new RelationalModelValidator( - new ModelValidatorDependencies( - typeMappingSource, - new MemberClassifier( - typeMappingSource, - TestServiceFactory.Instance.Create())), - new RelationalModelValidatorDependencies(typeMappingSource)); - } - public override LoggingDefinitions LoggingDefinitions { get; } = new TestRelationalLoggingDefinitions(); } } diff --git a/test/EFCore.Specification.Tests/TestUtilities/TestHelpers.cs b/test/EFCore.Specification.Tests/TestUtilities/TestHelpers.cs index 35e91011439..556dde1d76f 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/TestHelpers.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/TestHelpers.cs @@ -229,8 +229,6 @@ public ModelBuilder CreateConventionBuilder( return new ModelBuilder(conventionSet); } - public abstract IModelValidator CreateModelValidator(); - public virtual LoggingDefinitions LoggingDefinitions { get; } = new TestLoggingDefinitions(); public InternalEntityEntry CreateInternalEntry( diff --git a/test/EFCore.SqlServer.FunctionalTests/SqlServerDbFunctionMetadataTests.cs b/test/EFCore.SqlServer.FunctionalTests/SqlServerDbFunctionMetadataTests.cs index 5e07f5c075e..3f0db8fad06 100644 --- a/test/EFCore.SqlServer.FunctionalTests/SqlServerDbFunctionMetadataTests.cs +++ b/test/EFCore.SqlServer.FunctionalTests/SqlServerDbFunctionMetadataTests.cs @@ -69,7 +69,7 @@ private ModelBuilder GetModelBuilder() var conventionSet = new ConventionSet(); conventionSet.ModelAnnotationChangedConventions.Add( - new SqlServerDbFunctionConvention(CreateDependencies(), CreateRelationalDependencies())); + new SqlServerDbFunctionAttributeConvention(CreateDependencies(), CreateRelationalDependencies())); return new ModelBuilder(conventionSet); } diff --git a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerTestHelpers.cs b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerTestHelpers.cs index 741096a5c28..fc242e7756c 100644 --- a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerTestHelpers.cs +++ b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerTestHelpers.cs @@ -28,21 +28,6 @@ public override IServiceCollection AddProviderServices(IServiceCollection servic protected override void UseProviderOptions(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlServer(new SqlConnection("Database=DummyDatabase")); - public override IModelValidator CreateModelValidator() - { - var typeMappingSource = new SqlServerTypeMappingSource( - TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create()); - - return new SqlServerModelValidator( - new ModelValidatorDependencies( - typeMappingSource, - new MemberClassifier( - typeMappingSource, - TestServiceFactory.Instance.Create())), - new RelationalModelValidatorDependencies(typeMappingSource)); - } - public override LoggingDefinitions LoggingDefinitions { get; } = new SqlServerLoggingDefinitions(); } } diff --git a/test/EFCore.Sqlite.FunctionalTests/TestUtilities/SqliteTestHelpers.cs b/test/EFCore.Sqlite.FunctionalTests/TestUtilities/SqliteTestHelpers.cs index 85e25af29ac..a5c2b832d8f 100644 --- a/test/EFCore.Sqlite.FunctionalTests/TestUtilities/SqliteTestHelpers.cs +++ b/test/EFCore.Sqlite.FunctionalTests/TestUtilities/SqliteTestHelpers.cs @@ -28,21 +28,6 @@ public override IServiceCollection AddProviderServices(IServiceCollection servic protected override void UseProviderOptions(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlite(new SqliteConnection("Data Source=:memory:")); - public override IModelValidator CreateModelValidator() - { - var typeMappingSource = new SqliteTypeMappingSource( - TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create()); - - return new SqliteModelValidator( - new ModelValidatorDependencies( - typeMappingSource, - new MemberClassifier( - typeMappingSource, - TestServiceFactory.Instance.Create())), - new RelationalModelValidatorDependencies(typeMappingSource)); - } - public override LoggingDefinitions LoggingDefinitions { get; } = new SqliteLoggingDefinitions(); } } diff --git a/test/EFCore.Tests/Metadata/Conventions/Internal/PropertyMappingValidationConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/Internal/PropertyMappingValidationConventionTest.cs index 3062ade91c4..7fa96ce3b75 100644 --- a/test/EFCore.Tests/Metadata/Conventions/Internal/PropertyMappingValidationConventionTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/Internal/PropertyMappingValidationConventionTest.cs @@ -8,20 +8,22 @@ using System.Threading; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.InMemory.Storage.Internal; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.TestUtilities; using Xunit; // ReSharper disable InconsistentNaming namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal { - public class PropertyMappingValidationConventionTest + public class PropertyMappingValidationConventionTest : ModelValidatorTestBase { [Fact] public virtual void Throws_when_added_property_is_not_of_primitive_type() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); var entityTypeBuilder = modelBuilder.Entity(typeof(NonPrimitiveAsPropertyEntity), ConfigurationSource.Convention); entityTypeBuilder.Property(typeof(NavigationAsProperty), nameof(NonPrimitiveAsPropertyEntity.Property), ConfigurationSource.Convention); @@ -36,7 +38,7 @@ public virtual void Throws_when_added_property_is_not_of_primitive_type() [Fact] public virtual void Does_not_throw_when_added_shadow_property_by_convention_is_not_of_primitive_type() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); var entityTypeBuilder = modelBuilder.Entity(typeof(NonPrimitiveAsPropertyEntity), ConfigurationSource.Convention); entityTypeBuilder.Property(typeof(NavigationAsProperty), "ShadowProperty", ConfigurationSource.Convention); entityTypeBuilder.Ignore(nameof(NonPrimitiveAsPropertyEntity.Property), ConfigurationSource.Explicit); @@ -47,7 +49,7 @@ public virtual void Does_not_throw_when_added_shadow_property_by_convention_is_n [Fact] public virtual void Throws_when_primitive_type_property_is_not_added_or_ignored() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); modelBuilder.Entity(typeof(PrimitivePropertyEntity), ConfigurationSource.Convention); Assert.Equal( @@ -59,7 +61,7 @@ public virtual void Throws_when_primitive_type_property_is_not_added_or_ignored( [Fact] public virtual void Throws_when_nonprimitive_value_type_property_is_not_added_or_ignored() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); modelBuilder.Entity(typeof(NonPrimitiveValueTypePropertyEntity), ConfigurationSource.Convention); Assert.Equal( @@ -71,7 +73,7 @@ public virtual void Throws_when_nonprimitive_value_type_property_is_not_added_or [Fact] public virtual void Throws_when_keyless_type_property_is_not_added_or_ignored() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); modelBuilder.Entity(typeof(NonPrimitiveReferenceTypePropertyEntity), ConfigurationSource.Convention); Assert.Equal( @@ -85,7 +87,7 @@ public virtual void Throws_when_keyless_type_property_is_not_added_or_ignored() [Fact] public virtual void Does_not_throw_when_primitive_type_property_is_added() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); var entityTypeBuilder = modelBuilder.Entity(typeof(PrimitivePropertyEntity), ConfigurationSource.Convention); entityTypeBuilder.Property(typeof(int), "Property", ConfigurationSource.Convention); @@ -95,7 +97,7 @@ public virtual void Does_not_throw_when_primitive_type_property_is_added() [Fact] public virtual void Does_not_throw_when_primitive_type_property_is_ignored() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); var entityTypeBuilder = modelBuilder.Entity(typeof(PrimitivePropertyEntity), ConfigurationSource.Convention); entityTypeBuilder.Ignore("Property", ConfigurationSource.DataAnnotation); @@ -105,7 +107,7 @@ public virtual void Does_not_throw_when_primitive_type_property_is_ignored() [Fact] public virtual void Throws_when_navigation_is_not_added_or_ignored() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); modelBuilder.Entity(typeof(NavigationEntity), ConfigurationSource.Convention); modelBuilder.Entity(typeof(PrimitivePropertyEntity), ConfigurationSource.Convention); @@ -118,7 +120,7 @@ public virtual void Throws_when_navigation_is_not_added_or_ignored() [Fact] public virtual void Does_not_throw_when_navigation_is_added() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); var entityTypeBuilder = modelBuilder.Entity(typeof(NavigationEntity), ConfigurationSource.Convention); var referencedEntityTypeBuilder = modelBuilder.Entity(typeof(PrimitivePropertyEntity), ConfigurationSource.Convention); referencedEntityTypeBuilder.Ignore("Property", ConfigurationSource.DataAnnotation); @@ -130,7 +132,7 @@ public virtual void Does_not_throw_when_navigation_is_added() [Fact] public virtual void Does_not_throw_when_navigation_is_ignored() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); var entityTypeBuilder = modelBuilder.Entity(typeof(NavigationEntity), ConfigurationSource.Convention); entityTypeBuilder.Ignore("Navigation", ConfigurationSource.DataAnnotation); @@ -140,7 +142,7 @@ public virtual void Does_not_throw_when_navigation_is_ignored() [Fact] public virtual void Does_not_throw_when_navigation_target_entity_is_ignored() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); modelBuilder.Entity(typeof(NavigationEntity), ConfigurationSource.Convention); modelBuilder.Ignore(typeof(PrimitivePropertyEntity), ConfigurationSource.Convention); @@ -150,7 +152,7 @@ public virtual void Does_not_throw_when_navigation_target_entity_is_ignored() [Fact] public virtual void Does_not_throw_when_explicit_navigation_is_not_added() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); var entityTypeBuilder = modelBuilder.Entity(typeof(ExplicitNavigationEntity), ConfigurationSource.Convention); var referencedEntityTypeBuilder = modelBuilder.Entity(typeof(PrimitivePropertyEntity), ConfigurationSource.Convention); referencedEntityTypeBuilder.Ignore("Property", ConfigurationSource.DataAnnotation); @@ -163,7 +165,7 @@ public virtual void Does_not_throw_when_explicit_navigation_is_not_added() [Fact] public virtual void Throws_when_interface_type_property_is_not_added_or_ignored() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); modelBuilder.Entity(typeof(InterfaceNavigationEntity), ConfigurationSource.Convention); Assert.Equal( @@ -177,7 +179,7 @@ public virtual void Throws_when_interface_type_property_is_not_added_or_ignored( [Fact] public virtual void Does_not_throw_when_non_candidate_property_is_not_added() { - var modelBuilder = new InternalModelBuilder(new Model()); + var modelBuilder = CreateConventionlessModelBuilder().GetInfrastructure(); modelBuilder.Entity(typeof(NonCandidatePropertyEntity), ConfigurationSource.Convention); CreatePropertyMappingValidator()(modelBuilder.Metadata); @@ -185,7 +187,7 @@ public virtual void Does_not_throw_when_non_candidate_property_is_not_added() protected virtual Action CreatePropertyMappingValidator() { - var validator = TestHelpers.CreateModelValidator(); + var validator = CreateModelValidator(); var logger = new TestLogger(); var validatePropertyMappingMethod = typeof(ModelValidator).GetRuntimeMethods().Single(e => e.Name == "ValidatePropertyMapping"); @@ -207,7 +209,18 @@ protected virtual Action CreatePropertyMappingValidator() }; } - protected virtual TestHelpers TestHelpers => InMemoryTestHelpers.Instance; + protected virtual IModelValidator CreateModelValidator() + { + var typeMappingSource = new InMemoryTypeMappingSource( + TestServiceFactory.Instance.Create()); + + return new ModelValidator( + new ModelValidatorDependencies( + typeMappingSource, + new MemberClassifier( + typeMappingSource, + TestServiceFactory.Instance.Create()))); + } protected class NonPrimitiveNonNavigationAsPropertyEntity { diff --git a/test/EFCore.Tests/ModelSourceTest.cs b/test/EFCore.Tests/ModelSourceTest.cs index 8a65fee09e6..82ac7b664a2 100644 --- a/test/EFCore.Tests/ModelSourceTest.cs +++ b/test/EFCore.Tests/ModelSourceTest.cs @@ -13,6 +13,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Conventions; using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; using Microsoft.EntityFrameworkCore.TestUtilities; using Microsoft.Extensions.DependencyInjection; using Xunit; @@ -23,9 +24,6 @@ namespace Microsoft.EntityFrameworkCore { public class ModelSourceTest { - private readonly IModelValidator _coreModelValidator - = InMemoryTestHelpers.Instance.CreateContextServices().GetRequiredService(); - private readonly IConventionSetBuilder _nullConventionSetBuilder = new NullConventionSetBuilder(); @@ -76,10 +74,13 @@ protected internal override void OnConfiguring(DbContextOptionsBuilder optionsBu public void Adds_all_entities_based_on_all_distinct_entity_types_found() { var setFinder = new FakeSetFinder(); - var logger = new TestLogger(); var model = CreateDefaultModelSource(setFinder) - .GetModel(InMemoryTestHelpers.Instance.CreateContext(), _nullConventionSetBuilder); + .GetModel( + InMemoryTestHelpers.Instance.CreateContext(), + new RuntimeConventionSetBuilder(new ProviderConventionSetBuilder( + InMemoryTestHelpers.Instance.CreateContextServices().GetRequiredService() + .With(setFinder)), new List())); Assert.Equal( new[] { typeof(SetA).DisplayName(), typeof(SetB).DisplayName() }, @@ -126,7 +127,6 @@ private class SetB public void Caches_model_by_context_type() { var modelSource = CreateDefaultModelSource(new DbSetFinder()); - var logger = new TestLogger(); var model1 = modelSource.GetModel(new Context1(), _nullConventionSetBuilder); var model2 = modelSource.GetModel(new Context2(), _nullConventionSetBuilder); @@ -140,7 +140,6 @@ public void Caches_model_by_context_type() public void Stores_model_version_information_as_annotation_on_model() { var modelSource = CreateDefaultModelSource(new DbSetFinder()); - var logger = new TestLogger(); var model = modelSource.GetModel(new Context1(), _nullConventionSetBuilder); var packageVersion = typeof(Context1).Assembly.GetCustomAttributes()