Skip to content

Commit

Permalink
Move logic from ModelCustomizer to conventions.
Browse files Browse the repository at this point in the history
Part of #214
  • Loading branch information
AndriySvyryd committed Jun 6, 2019
1 parent a522e1e commit db25b57
Show file tree
Hide file tree
Showing 21 changed files with 185 additions and 243 deletions.
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

0 comments on commit db25b57

Please sign in to comment.