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

Use default property names for join entity type #21811

Merged
1 commit merged into from
Jul 28, 2020
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
8 changes: 7 additions & 1 deletion src/EFCore/Extensions/ConventionEntityTypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,13 @@ public static IConventionProperty AddProperty(
public static IConventionProperty AddProperty(
[NotNull] this IConventionEntityType entityType, [NotNull] string name, [NotNull] Type propertyType,
bool setTypeConfigurationSource = true, bool fromDataAnnotation = false)
=> entityType.AddProperty(name, propertyType, null, setTypeConfigurationSource, fromDataAnnotation);
=> ((EntityType)entityType).AddProperty(
name,
propertyType,
setTypeConfigurationSource
? fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention
: (ConfigurationSource?)null,
fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);

/// <summary>
/// Adds a property backed by and indexer to this entity type.
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/Extensions/MutableEntityTypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ public static IMutableProperty AddProperty(
/// <returns> The newly created property. </returns>
public static IMutableProperty AddProperty(
[NotNull] this IMutableEntityType entityType, [NotNull] string name, [NotNull] Type propertyType)
=> entityType.AddProperty(name, propertyType, null);
=> ((EntityType)entityType).AddProperty(name, propertyType, ConfigurationSource.Explicit, ConfigurationSource.Explicit);

/// <summary>
/// Adds a property backed up by an indexer to this entity type.
Expand Down
22 changes: 10 additions & 12 deletions src/EFCore/Metadata/Builders/CollectionCollectionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,21 +118,20 @@ public virtual EntityTypeBuilder UsingEntity(
Check.NotNull(configureRight, nameof(configureRight));
Check.NotNull(configureLeft, nameof(configureLeft));

var existingjoinEntityType = (EntityType)
var existingJoinEntityType = (EntityType)
(LeftNavigation.ForeignKey?.DeclaringEntityType
?? RightNavigation.ForeignKey?.DeclaringEntityType);
EntityType joinEntityType = null;
if (existingjoinEntityType != null)
if (existingJoinEntityType != null)
{
if (existingjoinEntityType.ClrType == joinEntityClrType
&& !existingjoinEntityType.HasSharedClrType)
if (existingJoinEntityType.ClrType == joinEntityClrType
&& !existingJoinEntityType.HasSharedClrType)
{
joinEntityType = existingjoinEntityType;
joinEntityType = existingJoinEntityType;
}
else
{
ModelBuilder.RemoveJoinEntityIfCreatedImplicitly(
existingjoinEntityType, removeSkipNavigations: false, ConfigurationSource.Explicit);
ModelBuilder.RemoveImplicitJoinEntity(existingJoinEntityType);
}
}

Expand Down Expand Up @@ -183,8 +182,7 @@ public virtual EntityTypeBuilder UsingEntity(
}
else
{
ModelBuilder.RemoveJoinEntityIfCreatedImplicitly(
existingJoinEntityType, removeSkipNavigations: false, ConfigurationSource.Explicit);
ModelBuilder.RemoveImplicitJoinEntity(existingJoinEntityType);
}
}

Expand Down Expand Up @@ -281,13 +279,13 @@ protected virtual void Using([NotNull] IMutableForeignKey rightForeignKey, [NotN
var leftBuilder = ((SkipNavigation)LeftNavigation).Builder;
var rightBuilder = ((SkipNavigation)RightNavigation).Builder;

leftBuilder = leftBuilder.HasInverse(rightBuilder.Metadata, ConfigurationSource.Explicit);

leftBuilder = leftBuilder.HasForeignKey((ForeignKey)leftForeignKey, ConfigurationSource.Explicit);
rightBuilder = rightBuilder.HasForeignKey((ForeignKey)rightForeignKey, ConfigurationSource.Explicit);

leftBuilder = leftBuilder.HasInverse(rightBuilder.Metadata, ConfigurationSource.Explicit);

LeftNavigation = leftBuilder.Metadata;
RightNavigation = leftBuilder.Metadata.Inverse;
RightNavigation = rightBuilder.Metadata;
}

#region Hidden System.Object members
Expand Down
6 changes: 2 additions & 4 deletions src/EFCore/Metadata/Builders/CollectionCollectionBuilder`.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,7 @@ public virtual EntityTypeBuilder<TJoinEntity> UsingEntity<TJoinEntity>(
}
else
{
ModelBuilder.RemoveJoinEntityIfCreatedImplicitly(
existingJoinEntityType, removeSkipNavigations: false, ConfigurationSource.Explicit);
ModelBuilder.RemoveImplicitJoinEntity(existingJoinEntityType);
}
}

Expand Down Expand Up @@ -139,8 +138,7 @@ public virtual EntityTypeBuilder<TJoinEntity> UsingEntity<TJoinEntity>(
}
else
{
ModelBuilder.RemoveJoinEntityIfCreatedImplicitly(
existingJoinEntityType, removeSkipNavigations: false, ConfigurationSource.Explicit);
ModelBuilder.RemoveImplicitJoinEntity(existingJoinEntityType);
}
}

Expand Down
13 changes: 8 additions & 5 deletions src/EFCore/Metadata/Builders/CollectionNavigationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,14 @@ private InternalForeignKeyBuilder WithOneBuilder(MemberIdentity reference)

var navigationName = SkipNavigation.Name;
var declaringEntityType = (EntityType)DeclaringEntityType;
declaringEntityType.Model.Builder
.RemoveJoinEntityIfCreatedImplicitly(
(EntityType)SkipNavigation.JoinEntityType,
removeSkipNavigations: true,
ConfigurationSource.Explicit);

if (SkipNavigation.Inverse != null)
{
((EntityType)SkipNavigation.Inverse.DeclaringEntityType).Builder.HasNoSkipNavigation(
(SkipNavigation)SkipNavigation.Inverse, ConfigurationSource.Explicit);
}

declaringEntityType.Builder.HasNoSkipNavigation((SkipNavigation)SkipNavigation, ConfigurationSource.Explicit);

Builder = declaringEntityType.Builder
.HasRelationship(
Expand Down
19 changes: 19 additions & 0 deletions src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,25 @@ IConventionEntityTypeBuilder HasNoRelationship(
/// <returns> <see langword="true" /> if the foreign key can be removed from this entity type. </returns>
bool CanRemoveRelationship([NotNull] IConventionForeignKey foreignKey, bool fromDataAnnotation = false);

/// <summary>
/// Removes a skip navigation from this entity type.
/// </summary>
/// <param name="skipNavigation"> The skip navigation to be removed. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns>
/// The same builder instance if the skip navigation was removed,
/// <see langword="null" /> otherwise.
/// </returns>
IConventionEntityTypeBuilder HasNoSkipNavigation([NotNull] ISkipNavigation skipNavigation, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the skip navigation can be removed from this entity type.
/// </summary>
/// <param name="skipNavigation"> The skip navigation to be removed. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> <see langword="true" /> if the skip navigation can be removed from this entity type. </returns>
bool CanRemoveSkipNavigation([NotNull] ISkipNavigation skipNavigation, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the given navigation can be added to this entity type.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,18 @@ public virtual ConventionSet CreateConventionSet()
conventionSet.NavigationAddedConventions.Add(relationshipDiscoveryConvention);
conventionSet.NavigationAddedConventions.Add(foreignKeyAttributeConvention);

var manyToManyJoinEntityTypeConvention = new ManyToManyJoinEntityTypeConvention(Dependencies);
conventionSet.SkipNavigationAddedConventions.Add(backingFieldConvention);
conventionSet.SkipNavigationAddedConventions.Add(manyToManyJoinEntityTypeConvention);

conventionSet.NavigationRemovedConventions.Add(relationshipDiscoveryConvention);
conventionSet.SkipNavigationRemovedConventions.Add(manyToManyJoinEntityTypeConvention);

conventionSet.SkipNavigationInverseChangedConventions.Add(manyToManyJoinEntityTypeConvention);

conventionSet.SkipNavigationAddedConventions.Add(new ManyToManyJoinEntityTypeConvention(Dependencies));
conventionSet.SkipNavigationForeignKeyChangedConventions.Add(manyToManyJoinEntityTypeConvention);
conventionSet.SkipNavigationForeignKeyChangedConventions.Add(keyDiscoveryConvention);

conventionSet.NavigationRemovedConventions.Add(relationshipDiscoveryConvention);

conventionSet.IndexAddedConventions.Add(foreignKeyIndexConvention);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -779,13 +779,6 @@ public override IConventionForeignKey OnSkipNavigationForeignKeyChanged(
_foreignKeyConventionContext.ResetState(foreignKey);
foreach (var skipNavigationConvention in _conventionSet.SkipNavigationForeignKeyChangedConventions)
{
if (navigationBuilder.Metadata.Builder == null
|| navigationBuilder.Metadata.ForeignKey != foreignKey)
{
Check.DebugAssert(false, "Foreign key changed");
return null;
}

skipNavigationConvention.ProcessSkipNavigationForeignKeyChanged(
navigationBuilder, foreignKey, oldForeignKey, _foreignKeyConventionContext);
if (_foreignKeyConventionContext.ShouldStopProcessing())
Expand Down Expand Up @@ -824,13 +817,6 @@ public override IConventionSkipNavigation OnSkipNavigationInverseChanged(
_skipNavigationConventionContext.ResetState(inverse);
foreach (var skipNavigationConvention in _conventionSet.SkipNavigationInverseChangedConventions)
{
if (navigationBuilder.Metadata.Builder == null
|| navigationBuilder.Metadata.Inverse != inverse)
{
Check.DebugAssert(false, "inverse changed");
return null;
}

skipNavigationConvention.ProcessSkipNavigationInverseChanged(
navigationBuilder, inverse, oldInverse, _skipNavigationConventionContext);
if (_skipNavigationConventionContext.ShouldStopProcessing())
Expand Down
90 changes: 37 additions & 53 deletions src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
Expand Down Expand Up @@ -37,7 +36,8 @@ public class KeyDiscoveryConvention :
IForeignKeyRemovedConvention,
IForeignKeyPropertiesChangedConvention,
IForeignKeyUniquenessChangedConvention,
IForeignKeyOwnershipChangedConvention
IForeignKeyOwnershipChangedConvention,
ISkipNavigationForeignKeyChangedConvention
{
private const string KeySuffix = "Id";

Expand Down Expand Up @@ -120,6 +120,16 @@ protected virtual void TryConfigurePrimaryKey([NotNull] IConventionEntityTypeBui
keyProperties.Add(extraProperty);
}

if (keyProperties.Count == 0)
{
var manyToManyForeignKeys = entityType.GetForeignKeys()
.Where(fk => fk.GetReferencingSkipNavigations().Any(n => n.IsCollection)).ToList();
if (manyToManyForeignKeys.Count() == 2)
{
keyProperties.AddRange(manyToManyForeignKeys.SelectMany(fk => fk.Properties));
}
}

ProcessKeyProperties(keyProperties, entityType);

if (keyProperties.Count > 0)
Expand Down Expand Up @@ -166,23 +176,13 @@ public static IEnumerable<IConventionProperty> DiscoverKeyProperties(
// ReSharper restore PossibleMultipleEnumeration
}

/// <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>
/// <inheritdoc />
public virtual void ProcessEntityTypeAdded(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionContext<IConventionEntityTypeBuilder> context)
=> TryConfigurePrimaryKey(entityTypeBuilder);

/// <summary>
/// Called after the base type of an entity type changes.
/// </summary>
/// <param name="entityTypeBuilder"> The builder for the entity type. </param>
/// <param name="newBaseType"> The new base entity type. </param>
/// <param name="oldBaseType"> The old base entity type. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
/// <inheritdoc />
public virtual void ProcessEntityTypeBaseTypeChanged(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionEntityType newBaseType,
Expand All @@ -197,23 +197,14 @@ public virtual void ProcessEntityTypeBaseTypeChanged(
TryConfigurePrimaryKey(entityTypeBuilder);
}

/// <summary>
/// Called after a property is added to the entity type.
/// </summary>
/// <param name="propertyBuilder"> The builder for the property. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
/// <inheritdoc />
public virtual void ProcessPropertyAdded(
IConventionPropertyBuilder propertyBuilder, IConventionContext<IConventionPropertyBuilder> context)
{
TryConfigurePrimaryKey(propertyBuilder.Metadata.DeclaringEntityType.Builder);
}

/// <summary>
/// Called after a key is removed.
/// </summary>
/// <param name="entityTypeBuilder"> The builder for the entity type. </param>
/// <param name="key"> The removed key. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
/// <inheritdoc />
public virtual void ProcessKeyRemoved(
IConventionEntityTypeBuilder entityTypeBuilder, IConventionKey key, IConventionContext<IConventionKey> context)
{
Expand All @@ -223,11 +214,7 @@ public virtual void ProcessKeyRemoved(
}
}

/// <summary>
/// Called after a foreign key is added to the entity type.
/// </summary>
/// <param name="relationshipBuilder"> The builder for the foreign key. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
/// <inheritdoc />
public virtual void ProcessForeignKeyAdded(
IConventionForeignKeyBuilder relationshipBuilder,
IConventionContext<IConventionForeignKeyBuilder> context)
Expand All @@ -238,13 +225,7 @@ public virtual void ProcessForeignKeyAdded(
}
}

/// <summary>
/// Called after the foreign key properties or principal key are changed.
/// </summary>
/// <param name="relationshipBuilder"> The builder for the foreign key. </param>
/// <param name="oldDependentProperties"> The old foreign key properties. </param>
/// <param name="oldPrincipalKey"> The old principal key. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
/// <inheritdoc />
public virtual void ProcessForeignKeyPropertiesChanged(
IConventionForeignKeyBuilder relationshipBuilder,
IReadOnlyList<IConventionProperty> oldDependentProperties,
Expand All @@ -260,24 +241,16 @@ public virtual void ProcessForeignKeyPropertiesChanged(
}
}

/// <summary>
/// Called after the ownership value for a foreign key is changed.
/// </summary>
/// <param name="relationshipBuilder"> The builder for the foreign key. </param>
/// <param name="context"> Additional information associated with convention execution. </param>

/// <inheritdoc />
public virtual void ProcessForeignKeyOwnershipChanged(
IConventionForeignKeyBuilder relationshipBuilder,
IConventionContext<bool?> context)
{
TryConfigurePrimaryKey(relationshipBuilder.Metadata.DeclaringEntityType.Builder);
}

/// <summary>
/// Called after a foreign key is removed.
/// </summary>
/// <param name="entityTypeBuilder"> The builder for the entity type. </param>
/// <param name="foreignKey"> The removed foreign key. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
/// <inheritdoc />
public virtual void ProcessForeignKeyRemoved(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionForeignKey foreignKey,
Expand All @@ -289,11 +262,7 @@ public virtual void ProcessForeignKeyRemoved(
}
}

/// <summary>
/// Called after the uniqueness for a foreign key is changed.
/// </summary>
/// <param name="relationshipBuilder"> The builder for the foreign key. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
/// <inheritdoc />
public virtual void ProcessForeignKeyUniquenessChanged(
IConventionForeignKeyBuilder relationshipBuilder,
IConventionContext<bool?> context)
Expand All @@ -303,5 +272,20 @@ public virtual void ProcessForeignKeyUniquenessChanged(
TryConfigurePrimaryKey(relationshipBuilder.Metadata.DeclaringEntityType.Builder);
}
}

/// <inheritdoc />
public virtual void ProcessSkipNavigationForeignKeyChanged(
IConventionSkipNavigationBuilder skipNavigationBuilder,
IConventionForeignKey foreignKey,
IConventionForeignKey oldForeignKey,
IConventionContext<IConventionForeignKey> context)
{
var joinEntityTypeBuilder = skipNavigationBuilder.Metadata.ForeignKey?.DeclaringEntityType.Builder;
if (joinEntityTypeBuilder != null
&& skipNavigationBuilder.Metadata.IsCollection)
{
TryConfigurePrimaryKey(joinEntityTypeBuilder);
}
}
}
}
Loading