Skip to content

Commit

Permalink
Reset ValueGenerated on owned types mapped to a different table.
Browse files Browse the repository at this point in the history
Fixes #17454
  • Loading branch information
AndriySvyryd committed Aug 29, 2019
1 parent fdba4f7 commit 554ec09
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 29 deletions.
Expand Up @@ -59,14 +59,6 @@ public override ConventionSet CreateConventionSet()
{
var conventionSet = base.CreateConventionSet();

ValueGenerationConvention valueGenerationConvention =
new RelationalValueGenerationConvention(Dependencies, RelationalDependencies);

ReplaceConvention(conventionSet.EntityTypeBaseTypeChangedConventions, valueGenerationConvention);
ReplaceConvention(conventionSet.EntityTypePrimaryKeyChangedConventions, valueGenerationConvention);
ReplaceConvention(conventionSet.ForeignKeyAddedConventions, valueGenerationConvention);
ReplaceConvention(conventionSet.ForeignKeyRemovedConventions, valueGenerationConvention);

var relationalColumnAttributeConvention = new RelationalColumnAttributeConvention(Dependencies, RelationalDependencies);

conventionSet.PropertyAddedConventions.Add(relationalColumnAttributeConvention);
Expand All @@ -75,8 +67,18 @@ public override ConventionSet CreateConventionSet()
conventionSet.EntityTypeAddedConventions.Add(new RelationalTableAttributeConvention(Dependencies, RelationalDependencies));
conventionSet.EntityTypeAddedConventions.Add(tableNameFromDbSetConvention);

ValueGenerationConvention valueGenerationConvention =
new RelationalValueGenerationConvention(Dependencies, RelationalDependencies);
ReplaceConvention(conventionSet.EntityTypeBaseTypeChangedConventions, valueGenerationConvention);
conventionSet.EntityTypeBaseTypeChangedConventions.Add(tableNameFromDbSetConvention);

conventionSet.EntityTypeAnnotationChangedConventions.Add((RelationalValueGenerationConvention)valueGenerationConvention);

ReplaceConvention(conventionSet.EntityTypePrimaryKeyChangedConventions, valueGenerationConvention);

ReplaceConvention(conventionSet.ForeignKeyAddedConventions, valueGenerationConvention);
ReplaceConvention(conventionSet.ForeignKeyRemovedConventions, valueGenerationConvention);

conventionSet.PropertyFieldChangedConventions.Add(relationalColumnAttributeConvention);

var storeGenerationConvention = new StoreGenerationConvention(Dependencies, RelationalDependencies);
Expand All @@ -87,21 +89,18 @@ public override ConventionSet CreateConventionSet()
conventionSet.ModelInitializedConventions.Add(dbFunctionAttributeConvention);
conventionSet.ModelAnnotationChangedConventions.Add(dbFunctionAttributeConvention);

var sharedTableConvention = new SharedTableConvention(Dependencies, RelationalDependencies);
ConventionSet.AddBefore(
conventionSet.ModelFinalizedConventions,
storeGenerationConvention,
typeof(ValidatingConvention));
ConventionSet.AddBefore(
conventionSet.ModelFinalizedConventions,
sharedTableConvention,
new SharedTableConvention(Dependencies, RelationalDependencies),
typeof(ValidatingConvention));

ConventionSet.AddBefore(
conventionSet.ModelFinalizedConventions,
new DbFunctionTypeMappingConvention(Dependencies, RelationalDependencies),
typeof(ValidatingConvention));

ReplaceConvention(
conventionSet.ModelFinalizedConventions,
(QueryFilterDefiningQueryRewritingConvention)new RelationalQueryFilterDefiningQueryRewritingConvention(
Expand Down
@@ -1,9 +1,11 @@
// 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.Linq;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
{
Expand All @@ -12,7 +14,8 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
/// part of the primary key and not part of any foreign keys or were configured to have a database default value.
/// It also configures properties as <see cref="ValueGenerated.OnAddOrUpdate" /> if they were configured as computed columns.
/// </summary>
public class RelationalValueGenerationConvention : ValueGenerationConvention, IPropertyAnnotationChangedConvention
public class RelationalValueGenerationConvention :
ValueGenerationConvention, IPropertyAnnotationChangedConvention, IEntityTypeAnnotationChangedConvention
{
/// <summary>
/// Creates a new instance of <see cref="RelationalValueGenerationConvention" />.
Expand Down Expand Up @@ -52,6 +55,59 @@ public class RelationalValueGenerationConvention : ValueGenerationConvention, IP
}
}

/// <summary>
/// Called after an annotation is changed on an entity type.
/// </summary>
/// <param name="entityTypeBuilder"> The builder for the entity type. </param>
/// <param name="name"> The annotation name. </param>
/// <param name="annotation"> The new annotation. </param>
/// <param name="oldAnnotation"> The old annotation. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
public virtual void ProcessEntityTypeAnnotationChanged(
IConventionEntityTypeBuilder entityTypeBuilder,
string name,
IConventionAnnotation annotation,
IConventionAnnotation oldAnnotation,
IConventionContext<IConventionAnnotation> context)
{
if (name == RelationalAnnotationNames.TableName)
{
ProcessTableChanged(
entityTypeBuilder,
(string)oldAnnotation?.Value ?? entityTypeBuilder.Metadata.GetDefaultTableName(),
entityTypeBuilder.Metadata.GetSchema());
} else if (name == RelationalAnnotationNames.Schema)
{
ProcessTableChanged(
entityTypeBuilder,
entityTypeBuilder.Metadata.GetTableName(),
(string)oldAnnotation?.Value ?? entityTypeBuilder.Metadata.GetDefaultSchema());
}
}

private void ProcessTableChanged(IConventionEntityTypeBuilder entityTypeBuilder, string oldTable, string oldSchema)
{
var pk = entityTypeBuilder.Metadata.FindPrimaryKey();
if (pk == null)
{
return;
}

var oldLink = pk.Properties.First().FindSharedTableLink(oldTable, oldSchema);
var newLink = pk.Properties.First().FindSharedTableLink();

if (oldLink == null
&& newLink == null)
{
return;
}

foreach (var property in pk.Properties)
{
property.Builder.ValueGenerated(GetValueGenerated(property));
}
}

/// <summary>
/// Returns the store value generation strategy to set for the given property.
/// </summary>
Expand Down
Expand Up @@ -21,6 +21,15 @@ public static class RelationalPropertyExtensions
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static IForeignKey FindSharedTableLink([NotNull] this IProperty property)
=> property.FindSharedTableLink(property.DeclaringEntityType.GetTableName(), property.DeclaringEntityType.GetSchema());

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static IForeignKey FindSharedTableLink([NotNull] this IProperty property, string table, string schema)
{
var pk = property.FindContainingPrimaryKey();
if (pk == null)
Expand All @@ -40,8 +49,8 @@ public static IForeignKey FindSharedTableLink([NotNull] this IProperty property)

var principalEntityType = fk.PrincipalEntityType;
var declaringEntityType = fk.DeclaringEntityType;
if (declaringEntityType.GetTableName() == principalEntityType.GetTableName()
&& declaringEntityType.GetSchema() == principalEntityType.GetSchema())
if (table == principalEntityType.GetTableName()
&& schema == principalEntityType.GetSchema())
{
return fk;
}
Expand Down
Expand Up @@ -55,10 +55,13 @@ public override ConventionSet CreateConventionSet()

ValueGenerationConvention valueGenerationConvention =
new SqlServerValueGenerationConvention(Dependencies, RelationalDependencies);
var sqlServerIndexConvention = new SqlServerIndexConvention(Dependencies, RelationalDependencies, _sqlGenerationHelper);
ReplaceConvention(conventionSet.EntityTypeBaseTypeChangedConventions, valueGenerationConvention);
conventionSet.EntityTypeBaseTypeChangedConventions.Add(sqlServerIndexConvention);

var sqlServerInMemoryTablesConvention = new SqlServerMemoryOptimizedTablesConvention(Dependencies, RelationalDependencies);
conventionSet.EntityTypeAnnotationChangedConventions.Add(sqlServerInMemoryTablesConvention);
ReplaceConvention(conventionSet.EntityTypeAnnotationChangedConventions, (RelationalValueGenerationConvention)valueGenerationConvention);

ReplaceConvention(conventionSet.EntityTypePrimaryKeyChangedConventions, valueGenerationConvention);

Expand All @@ -68,15 +71,6 @@ public override ConventionSet CreateConventionSet()

ReplaceConvention(conventionSet.ForeignKeyRemovedConventions, valueGenerationConvention);

var sqlServerIndexConvention = new SqlServerIndexConvention(Dependencies, RelationalDependencies, _sqlGenerationHelper);

conventionSet.EntityTypeBaseTypeChangedConventions.Add(sqlServerIndexConvention);

ConventionSet.AddBefore(
conventionSet.ModelFinalizedConventions,
valueGenerationStrategyConvention,
typeof(ValidatingConvention));

conventionSet.IndexAddedConventions.Add(sqlServerInMemoryTablesConvention);
conventionSet.IndexAddedConventions.Add(sqlServerIndexConvention);

Expand All @@ -93,6 +87,10 @@ public override ConventionSet CreateConventionSet()
ReplaceConvention(
conventionSet.PropertyAnnotationChangedConventions, (RelationalValueGenerationConvention)valueGenerationConvention);

ConventionSet.AddBefore(
conventionSet.ModelFinalizedConventions,
valueGenerationStrategyConvention,
typeof(ValidatingConvention));
ReplaceConvention(conventionSet.ModelFinalizedConventions, storeGenerationConvention);

return conventionSet;
Expand Down
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Xunit;

Expand Down Expand Up @@ -398,22 +399,38 @@ public virtual void Owned_types_can_be_mapped_to_different_tables()

Assert.NotSame(bookOwnership1.DeclaringEntityType, bookOwnership2.DeclaringEntityType);
Assert.Equal(1, bookOwnership1.DeclaringEntityType.GetForeignKeys().Count());
Assert.Equal(1, bookOwnership1.DeclaringEntityType.GetForeignKeys().Count());
Assert.Equal(1, bookOwnership2.DeclaringEntityType.GetForeignKeys().Count());

Assert.NotSame(bookLabel1Ownership1.DeclaringEntityType, bookLabel2Ownership1.DeclaringEntityType);
Assert.NotSame(bookLabel1Ownership2.DeclaringEntityType, bookLabel2Ownership2.DeclaringEntityType);
Assert.Equal(1, bookLabel1Ownership1.DeclaringEntityType.GetForeignKeys().Count());
Assert.Equal(1, bookLabel1Ownership2.DeclaringEntityType.GetForeignKeys().Count());
Assert.Equal(1, bookLabel2Ownership1.DeclaringEntityType.GetForeignKeys().Count());
Assert.Equal(1, bookLabel2Ownership2.DeclaringEntityType.GetForeignKeys().Count());
Assert.Equal(1, bookLabel1Ownership1.DeclaringEntityType.GetForeignKeys().Count());
Assert.Equal(1, bookLabel1Ownership2.DeclaringEntityType.GetForeignKeys().Count());
Assert.Equal(1, bookLabel2Ownership1.DeclaringEntityType.GetForeignKeys().Count());
Assert.Equal(1, bookLabel2Ownership2.DeclaringEntityType.GetForeignKeys().Count());

Assert.NotSame(bookLabel1Ownership11.DeclaringEntityType, bookLabel2Ownership11.DeclaringEntityType);
Assert.NotSame(bookLabel1Ownership21.DeclaringEntityType, bookLabel2Ownership21.DeclaringEntityType);
Assert.Equal(1, bookLabel1Ownership11.DeclaringEntityType.GetForeignKeys().Count());
Assert.Equal(1, bookLabel1Ownership21.DeclaringEntityType.GetForeignKeys().Count());
Assert.Equal(1, bookLabel2Ownership11.DeclaringEntityType.GetForeignKeys().Count());
Assert.Equal(1, bookLabel2Ownership21.DeclaringEntityType.GetForeignKeys().Count());

Assert.Equal(2, model.GetEntityTypes().Count(e => e.ClrType == typeof(BookLabel)));
Assert.Equal(4, model.GetEntityTypes().Count(e => e.ClrType == typeof(AnotherBookLabel)));
Assert.Equal(4, model.GetEntityTypes().Count(e => e.ClrType == typeof(SpecialBookLabel)));

Assert.Equal(ValueGenerated.Never, bookOwnership1.DeclaringEntityType.FindPrimaryKey().Properties.Single().ValueGenerated);
Assert.Equal(ValueGenerated.Never, bookOwnership2.DeclaringEntityType.FindPrimaryKey().Properties.Single().ValueGenerated);

Assert.Equal(ValueGenerated.Never, bookLabel1Ownership1.DeclaringEntityType.FindPrimaryKey().Properties.Single().ValueGenerated);
Assert.Equal(ValueGenerated.Never, bookLabel1Ownership2.DeclaringEntityType.FindPrimaryKey().Properties.Single().ValueGenerated);
Assert.Equal(ValueGenerated.Never, bookLabel2Ownership1.DeclaringEntityType.FindPrimaryKey().Properties.Single().ValueGenerated);
Assert.Equal(ValueGenerated.Never, bookLabel2Ownership2.DeclaringEntityType.FindPrimaryKey().Properties.Single().ValueGenerated);

Assert.Equal(ValueGenerated.Never, bookLabel1Ownership11.DeclaringEntityType.FindPrimaryKey().Properties.Single().ValueGenerated);
Assert.Equal(ValueGenerated.Never, bookLabel1Ownership21.DeclaringEntityType.FindPrimaryKey().Properties.Single().ValueGenerated);
Assert.Equal(ValueGenerated.Never, bookLabel2Ownership11.DeclaringEntityType.FindPrimaryKey().Properties.Single().ValueGenerated);
Assert.Equal(ValueGenerated.Never, bookLabel2Ownership21.DeclaringEntityType.FindPrimaryKey().Properties.Single().ValueGenerated);
}

[ConditionalFact]
Expand Down

0 comments on commit 554ec09

Please sign in to comment.