Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move logic from ModelCustomizer to conventions. #15982

Merged
merged 1 commit into from
Jun 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// <para>
/// Builds the model for a given context. This default implementation builds the model by calling
/// <see cref="DbContext.OnConfiguring(DbContextOptionsBuilder)" /> on the context.
/// </para>
/// <para>
/// Also, entity types found as <see cref="DbSet{TEntity}" /> properties on the context are mapped
/// to tables named for the DbSet property names, and public static methods on the context marked with
/// <see cref="DbFunctionAttribute" /> are mapped to database functions.
/// Builds the model for a given context.
/// </para>
/// <para>
/// This type is typically used by database providers (and other extensions). It is generally
Expand All @@ -42,80 +30,5 @@ public RelationalModelCustomizer([NotNull] ModelCustomizerDependencies dependenc
: base(dependencies)
{
}

/// <summary>
/// <para>
/// 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 <see cref="DbContext.OnConfiguring(DbContextOptionsBuilder)" />
/// on the context.
/// </para>
/// <para>
/// Also, entity types found as <see cref="DbSet{TEntity}" /> properties on the context are mapped
/// to tables named for the DbSet property names, and public static methods on the context marked with
/// <see cref="DbFunctionAttribute" /> are mapped to database functions.
/// </para>
/// </summary>
/// <param name="modelBuilder">
/// The builder being used to construct the model.
/// </param>
/// <param name="context">
/// The context instance that the model is being created for.
/// </param>
public override void Customize(ModelBuilder modelBuilder, DbContext context)
{
FindDbFunctions(modelBuilder, context);

base.Customize(modelBuilder, context);
}

/// <summary>
/// Adds to the model function mappings found as public static methods on the context marked with
/// the <see cref="DbFunctionAttribute" />.
/// </summary>
/// <param name="modelBuilder"> The <see cref="ModelBuilder" /> being used to build the model. </param>
/// <param name="context"> The context to find function methods on. </param>
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;
}
}

/// <summary>
/// Adds the entity types found in <see cref="DbSet{TEntity}" /> properties on the context to the model.
/// </summary>
/// <param name="modelBuilder"> The <see cref="ModelBuilder" /> being used to build the model. </param>
/// <param name="contextType"> The context type to find <see cref="DbSet{TEntity}" /> properties on. </param>
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<IConventionEntityType>())
{
if (entityType.BaseType == null
&& sets.ContainsKey(entityType.ClrType))
{
entityType.Builder.ToTable(sets[entityType.ClrType].Name);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -87,7 +95,7 @@ public override ConventionSet CreateConventionSet()
sharedTableConvention,
typeof(ValidatingConvention));

conventionSet.ModelAnnotationChangedConventions.Add(new RelationalDbFunctionConvention(Dependencies, RelationalDependencies));
conventionSet.ModelAnnotationChangedConventions.Add(dbFunctionAttributeConvention);

return conventionSet;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@
namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
{
/// <summary>
/// A convention that configures the name and schema for a <see cref="IDbFunction"/> based on the applied
/// A convention that configures model function mappings based on public static methods on the context marked with
/// <see cref="DbFunctionAttribute"/>.
/// </summary>
public class RelationalDbFunctionConvention : IModelAnnotationChangedConvention
public class RelationalDbFunctionAttributeConvention : IModelInitializedConvention, IModelAnnotationChangedConvention
{
/// <summary>
/// Creates a new instance of <see cref="RelationalDbFunctionConvention" />.
/// Creates a new instance of <see cref="RelationalDbFunctionAttributeConvention" />.
/// </summary>
/// <param name="dependencies"> Parameter object containing dependencies for this convention. </param>
/// <param name="relationalDependencies"> Parameter object containing relational dependencies for this convention. </param>
public RelationalDbFunctionConvention(
public RelationalDbFunctionAttributeConvention(
[NotNull] ProviderConventionSetBuilderDependencies dependencies,
[NotNull] RelationalConventionSetBuilderDependencies relationalDependencies)
{
Expand All @@ -34,6 +34,31 @@ public RelationalDbFunctionConvention(
/// </summary>
protected virtual ProviderConventionSetBuilderDependencies Dependencies { get; }

/// <summary>
/// Called after a model is initialized.
/// </summary>
/// <param name="modelBuilder"> The builder for the model. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
public virtual void ProcessModelInitialized(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> 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;
}
}

/// <summary>
/// Called after an annotation is changed on an model.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
/// <summary>
/// A convention that configures the table name based on the <see cref="DbSet{TEntity}"/> property name.
/// </summary>
public class TableNameFromDbSetConvention : IEntityTypeBaseTypeChangedConvention
public class TableNameFromDbSetConvention : IEntityTypeAddedConvention, IEntityTypeBaseTypeChangedConvention
{
private readonly IDictionary<Type, DbSetProperty> _sets;

Expand Down Expand Up @@ -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);
}
}
}

/// <summary>
/// Called after an entity type is added to the model.
/// </summary>
/// <param name="entityTypeBuilder"> The builder for the entity type. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
public virtual void ProcessEntityTypeAdded(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionContext<IConventionEntityTypeBuilder> context)
{
var entityType = entityTypeBuilder.Metadata;
if (entityType.BaseType == null
&& entityType.ClrType != null
&& _sets.ContainsKey(entityType.ClrType))
{
entityTypeBuilder.ToTable(_sets[entityType.ClrType].Name);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@
namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
{
/// <summary>
/// A convention that configures the name and schema for a <see cref="IDbFunction"/> based on the applied
/// <see cref="DbFunctionAttribute"/> and the default schema as 'dbo'.
/// A convention that configures model function mappings based on public static methods on the context marked with
/// <see cref="DbFunctionAttribute"/> and sets the default schema to 'dbo'.
/// </summary>
public class SqlServerDbFunctionConvention : RelationalDbFunctionConvention
public class SqlServerDbFunctionAttributeConvention : RelationalDbFunctionAttributeConvention
{
/// <summary>
/// Creates a new instance of <see cref="SqlServerDbFunctionConvention" />.
/// Creates a new instance of <see cref="SqlServerDbFunctionAttributeConvention" />.
/// </summary>
/// <param name="dependencies"> Parameter object containing dependencies for this convention. </param>
/// <param name="relationalDependencies"> Parameter object containing relational dependencies for this convention. </param>
public SqlServerDbFunctionConvention(
public SqlServerDbFunctionAttributeConvention(
[NotNull] ProviderConventionSetBuilderDependencies dependencies,
[NotNull] RelationalConventionSetBuilderDependencies relationalDependencies)
: base(dependencies, relationalDependencies)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class SqlServerIndexConvention :
private readonly ISqlGenerationHelper _sqlGenerationHelper;

/// <summary>
/// Creates a new instance of <see cref="SqlServerDbFunctionConvention" />.
/// Creates a new instance of <see cref="SqlServerDbFunctionAttributeConvention" />.
/// </summary>
/// <param name="dependencies"> Parameter object containing dependencies for this convention. </param>
/// <param name="relationalDependencies"> Parameter object containing relational dependencies for this convention. </param>
Expand Down
23 changes: 0 additions & 23 deletions src/EFCore/Infrastructure/ModelCustomizer.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -54,29 +53,7 @@ public ModelCustomizer([NotNull] ModelCustomizerDependencies dependencies)
/// </param>
public virtual void Customize(ModelBuilder modelBuilder, DbContext context)
{
FindSets(modelBuilder, context.GetType());

context.OnModelCreating(modelBuilder);
}

/// <summary>
/// Adds the entity types found in <see cref="DbSet{TEntity}" /> properties on the context to the model.
/// </summary>
/// <param name="modelBuilder"> The <see cref="ModelBuilder" /> being used to build the model. </param>
/// <param name="contextType"> The context type to find <see cref="DbSet{TEntity}" /> properties on. </param>
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);
}
}
}
}
}
50 changes: 50 additions & 0 deletions src/EFCore/Metadata/Conventions/DbSetFindingConvention.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// A convention that adds entity types based on the <see cref="DbSet{TEntity}"/> properties defined on the
/// derived <see cref="DbContext"/> class.
/// </summary>
public class DbSetFindingConvention : IModelInitializedConvention
{
/// <summary>
/// Creates a new instance of <see cref="DbSetFindingConvention" />.
/// </summary>
/// <param name="dependencies"> Parameter object containing dependencies for this convention. </param>
public DbSetFindingConvention([NotNull] ProviderConventionSetBuilderDependencies dependencies)
{
Dependencies = dependencies;
}

/// <summary>
/// Parameter object containing service dependencies.
/// </summary>
protected virtual ProviderConventionSetBuilderDependencies Dependencies { get; }

/// <summary>
/// Called after a model is initialized.
/// </summary>
/// <param name="modelBuilder"> The builder for the model. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
public virtual void ProcessModelInitialized(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> 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);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading