Skip to content

Commit

Permalink
Add skip navigation support in Metadata
Browse files Browse the repository at this point in the history
Part of #19003
  • Loading branch information
AndriySvyryd committed Dec 20, 2019
1 parent dc1e811 commit 2aefa91
Show file tree
Hide file tree
Showing 56 changed files with 3,856 additions and 483 deletions.
4 changes: 2 additions & 2 deletions src/EFCore/Extensions/ConventionNavigationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static class ConventionNavigationExtensions
/// The inverse navigation, or <c>null</c> if none is defined.
/// </returns>
public static IConventionNavigation FindInverse([NotNull] this IConventionNavigation navigation)
=> (IConventionNavigation)((INavigation)navigation).FindInverse();
=> ((Navigation)navigation).FindInverse();

/// <summary>
/// Gets the entity type that a given navigation property will hold an instance of
Expand All @@ -31,7 +31,7 @@ public static IConventionNavigation FindInverse([NotNull] this IConventionNaviga
/// <param name="navigation"> The navigation property to find the target entity type of. </param>
/// <returns> The target entity type. </returns>
public static IConventionEntityType GetTargetType([NotNull] this IConventionNavigation navigation)
=> (IConventionEntityType)((INavigation)navigation).GetTargetType();
=> ((Navigation)navigation).GetTargetType();

/// <summary>
/// Sets a value indicating whether this navigation should be eager loaded by default.
Expand Down
11 changes: 3 additions & 8 deletions src/EFCore/Extensions/EntityTypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ public static IKey FindKey([NotNull] this IEntityType entityType, [NotNull] IPro
/// <param name="property"> The property to find the foreign keys on. </param>
/// <returns> The foreign keys. </returns>
public static IEnumerable<IForeignKey> FindForeignKeys([NotNull] this IEntityType entityType, [NotNull] IProperty property)
=> entityType.FindForeignKeys(new[] { property });
=> property.GetContainingForeignKeys();

/// <summary>
/// Gets the foreign keys defined on the given properties. Only foreign keys that are defined on exactly the specified
Expand All @@ -413,13 +413,8 @@ public static IEnumerable<IForeignKey> FindForeignKeys(
Check.NotEmpty(properties, nameof(properties));
Check.HasNoNulls(properties, nameof(properties));

foreach (var foreignKey in entityType.GetForeignKeys())
{
if (PropertyListComparer.Instance.Equals(foreignKey.Properties, properties))
{
yield return foreignKey;
}
}
return entityType.GetForeignKeys()
.Where(foreignKey => PropertyListComparer.Instance.Equals(foreignKey.Properties, properties));
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions src/EFCore/Extensions/MutableNavigationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static class MutableNavigationExtensions
/// The inverse navigation, or <c>null</c> if none is defined.
/// </returns>
public static IMutableNavigation FindInverse([NotNull] this IMutableNavigation navigation)
=> (IMutableNavigation)((INavigation)navigation).FindInverse();
=> ((Navigation)navigation).FindInverse();

/// <summary>
/// Gets the entity type that a given navigation property will hold an instance of
Expand All @@ -31,7 +31,7 @@ public static IMutableNavigation FindInverse([NotNull] this IMutableNavigation n
/// <param name="navigation"> The navigation property to find the target entity type of. </param>
/// <returns> The target entity type. </returns>
public static IMutableEntityType GetTargetType([NotNull] this IMutableNavigation navigation)
=> (IMutableEntityType)((INavigation)navigation).GetTargetType();
=> ((Navigation)navigation).GetTargetType();

/// <summary>
/// Sets a value indicating whether this navigation should be eager loaded by default.
Expand Down
16 changes: 2 additions & 14 deletions src/EFCore/Extensions/NavigationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,7 @@ public static bool IsCollection([NotNull] this INavigation navigation)
/// </returns>
[DebuggerStepThrough]
public static INavigation FindInverse([NotNull] this INavigation navigation)
{
Check.NotNull(navigation, nameof(navigation));

return navigation.IsDependentToPrincipal()
? navigation.ForeignKey.PrincipalToDependent
: navigation.ForeignKey.DependentToPrincipal;
}
=> ((Navigation)Check.NotNull(navigation, nameof(navigation))).FindInverse();

/// <summary>
/// Gets the entity type that a given navigation property will hold an instance of
Expand All @@ -78,13 +72,7 @@ public static INavigation FindInverse([NotNull] this INavigation navigation)
/// <returns> The target entity type. </returns>
[DebuggerStepThrough]
public static IEntityType GetTargetType([NotNull] this INavigation navigation)
{
Check.NotNull(navigation, nameof(navigation));

return navigation.IsDependentToPrincipal()
? navigation.ForeignKey.PrincipalEntityType
: navigation.ForeignKey.DeclaringEntityType;
}
=> (Check.NotNull(navigation, nameof(navigation)) as Navigation)?.GetTargetType();

/// <summary>
/// Gets a value indicating whether this navigation should be eager loaded by default.
Expand Down
16 changes: 16 additions & 0 deletions src/EFCore/Infrastructure/ModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@ protected virtual void ValidateRelationships(
: "." + foreignKey.PrincipalToDependent.Name)));
}
}

foreach (var navigation in entityType.GetDeclaredSkipNavigations())
{
if (!navigation.IsCollection)
{
throw new InvalidOperationException(CoreStrings.SkipNavigationNonCollection(
navigation.Name, navigation.DeclaringEntityType.DisplayName()));
}

if (navigation.Inverse == null)
{
throw new InvalidOperationException(CoreStrings.SkipNavigationNoInverse(
navigation.Name, navigation.DeclaringEntityType.DisplayName()));
}
}
}
}

Expand Down Expand Up @@ -155,6 +170,7 @@ protected virtual void ValidatePropertyMapping(

clrProperties.ExceptWith(entityType.GetProperties().Select(p => p.Name));
clrProperties.ExceptWith(entityType.GetNavigations().Select(p => p.Name));
clrProperties.ExceptWith(entityType.GetSkipNavigations().Select(p => p.Name));
clrProperties.ExceptWith(entityType.GetServiceProperties().Select(p => p.Name));
clrProperties.RemoveWhere(p => entityType.FindIgnoredConfigurationSource(p) != null);

Expand Down
22 changes: 22 additions & 0 deletions src/EFCore/Metadata/Builders/IConventionSkipNavigationBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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.

namespace Microsoft.EntityFrameworkCore.Metadata.Builders
{
/// <summary>
/// <para>
/// Provides a simple API surface for configuring an <see cref="IConventionSkipNavigation" /> from conventions.
/// </para>
/// <para>
/// This interface is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// </summary>
public interface IConventionSkipNavigationBuilder : IConventionAnnotatableBuilder
{
/// <summary>
/// The navigation property being configured.
/// </summary>
new IConventionSkipNavigation Metadata { get; }
}
}
35 changes: 35 additions & 0 deletions src/EFCore/Metadata/Conventions/ConventionSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,41 @@ public class ConventionSet
/// </summary>
public virtual IList<INavigationAddedConvention> NavigationAddedConventions { get; } = new List<INavigationAddedConvention>();

/// <summary>
/// Conventions to run when an annotation is changed on a navigation property.
/// </summary>
public virtual IList<INavigationAnnotationChangedConvention> NavigationAnnotationChangedConventions { get; }
= new List<INavigationAnnotationChangedConvention>();

/// <summary>
/// Conventions to run when a navigation property is removed.
/// </summary>
public virtual IList<INavigationRemovedConvention> NavigationRemovedConventions { get; } = new List<INavigationRemovedConvention>();

/// <summary>
/// Conventions to run when a skip navigation property is added.
/// </summary>
public virtual IList<ISkipNavigationAddedConvention> SkipNavigationAddedConventions { get; }
= new List<ISkipNavigationAddedConvention>();

/// <summary>
/// Conventions to run when an annotation is changed on a skip navigation property.
/// </summary>
public virtual IList<ISkipNavigationAnnotationChangedConvention> SkipNavigationAnnotationChangedConventions { get; }
= new List<ISkipNavigationAnnotationChangedConvention>();

/// <summary>
/// Conventions to run when a skip navigation inverse is changed.
/// </summary>
public virtual IList<ISkipNavigationInverseChangedConvention> SkipNavigationInverseChangedConventions { get; }
= new List<ISkipNavigationInverseChangedConvention>();

/// <summary>
/// Conventions to run when a skip navigation property is removed.
/// </summary>
public virtual IList<ISkipNavigationRemovedConvention> SkipNavigationRemovedConventions { get; }
= new List<ISkipNavigationRemovedConvention>();

/// <summary>
/// Conventions to run when a key is added.
/// </summary>
Expand Down Expand Up @@ -187,6 +217,11 @@ public class ConventionSet
public virtual IList<IPropertyAnnotationChangedConvention> PropertyAnnotationChangedConventions { get; }
= new List<IPropertyAnnotationChangedConvention>();

/// <summary>
/// Conventions to run when a property is removed.
/// </summary>
public virtual IList<IPropertyRemovedConvention> PropertyRemovedConventions { get; } = new List<IPropertyRemovedConvention>();

/// <summary>
/// Replaces an existing convention with a derived convention.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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;

namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
{
/// <summary>
/// Represents an operation that should be performed when an annotation is changed on a navigation.
/// </summary>
public interface INavigationAnnotationChangedConvention : IConvention
{
/// <summary>
/// Called after an annotation is changed on a navigation.
/// </summary>
/// <param name="relationshipBuilder"> The builder for the foreign key. </param>
/// <param name="navigation"> The navigation. </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>
void ProcessNavigationAnnotationChanged(
[NotNull] IConventionRelationshipBuilder relationshipBuilder,
[NotNull] IConventionNavigation navigation,
[NotNull] string name,
[CanBeNull] IConventionAnnotation annotation,
[CanBeNull] IConventionAnnotation oldAnnotation,
[NotNull] IConventionContext<IConventionAnnotation> context);
}
}
25 changes: 25 additions & 0 deletions src/EFCore/Metadata/Conventions/IPropertyRemovedConvention.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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;

namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
{
/// <summary>
/// Represents an operation that should be performed when a property is removed from the entity type.
/// </summary>
public interface IPropertyRemovedConvention : IConvention
{
/// <summary>
/// Called after a property is removed from the entity type.
/// </summary>
/// <param name="entityTypeBuilder"> The builder for the entity type that contained the property. </param>
/// <param name="property"> The removed property. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
void ProcessPropertyRemoved(
[NotNull] IConventionEntityTypeBuilder entityTypeBuilder,
[NotNull] IConventionProperty property,
[NotNull] IConventionContext<IConventionProperty> context);
}
}
23 changes: 23 additions & 0 deletions src/EFCore/Metadata/Conventions/ISkipNavigationAddedConvention.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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;

namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
{
/// <summary>
/// Represents an operation that should be performed when a skip navigation is added to the entity type.
/// </summary>
public interface ISkipNavigationAddedConvention : IConvention
{
/// <summary>
/// Called after a skip navigation is added to the entity type.
/// </summary>
/// <param name="skipNavigationBuilder"> The builder for the skip navigation. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
void ProcessSkipNavigationAdded(
[NotNull] IConventionSkipNavigationBuilder skipNavigationBuilder,
[NotNull] IConventionContext<IConventionSkipNavigationBuilder> context);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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;

namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
{
/// <summary>
/// Represents an operation that should be performed when an annotation is changed on a skip navigation.
/// </summary>
public interface ISkipNavigationAnnotationChangedConvention : IConvention
{
/// <summary>
/// Called after an annotation is changed on a skip navigation.
/// </summary>
/// <param name="skipNavigationBuilder"> The builder for the skip navigation. </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>
void ProcessSkipNavigationAnnotationChanged(
[NotNull] IConventionSkipNavigationBuilder skipNavigationBuilder,
[NotNull] string name,
[CanBeNull] IConventionAnnotation annotation,
[CanBeNull] IConventionAnnotation oldAnnotation,
[NotNull] IConventionContext<IConventionAnnotation> context);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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;

namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
{
/// <summary>
/// Represents an operation that should be performed when a skip navigation inverse is changed.
/// </summary>
public interface ISkipNavigationInverseChangedConvention : IConvention
{
/// <summary>
/// Called after a skip navigation inverse is changed.
/// </summary>
/// <param name="skipNavigationBuilder"> The builder for the skip navigation. </param>
/// <param name="inverse"> The current inverse skip navigation. </param>
/// <param name="oldInverse"> The old inverse skip navigation. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
void ProcessSkipNavigationInverseChanged(
[NotNull] IConventionSkipNavigationBuilder skipNavigationBuilder,
[NotNull] IConventionSkipNavigation inverse,
[NotNull] IConventionSkipNavigation oldInverse,
[NotNull] IConventionContext<IConventionSkipNavigation> context);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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;

namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
{
/// <summary>
/// Represents an operation that should be performed when a skip navigation is removed from the entity type.
/// </summary>
public interface ISkipNavigationRemovedConvention : IConvention
{
/// <summary>
/// Called after a skip navigation is removed from the entity type.
/// </summary>
/// <param name="entityTypeBuilder"> The builder for the entity type that contained the navigation. </param>
/// <param name="navigation"> The removed navigation. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
void ProcessSkipNavigationRemoved(
[NotNull] IConventionEntityTypeBuilder entityTypeBuilder,
[NotNull] IConventionSkipNavigation navigation,
[NotNull] IConventionContext<IConventionSkipNavigation> context);
}
}
Loading

0 comments on commit 2aefa91

Please sign in to comment.