diff --git a/src/EFCore/Extensions/ConventionNavigationExtensions.cs b/src/EFCore/Extensions/ConventionNavigationExtensions.cs
index e4caa0f6b8a..73b5cec61f5 100644
--- a/src/EFCore/Extensions/ConventionNavigationExtensions.cs
+++ b/src/EFCore/Extensions/ConventionNavigationExtensions.cs
@@ -22,7 +22,7 @@ public static class ConventionNavigationExtensions
/// The inverse navigation, or null if none is defined.
///
public static IConventionNavigation FindInverse([NotNull] this IConventionNavigation navigation)
- => (IConventionNavigation)((INavigation)navigation).FindInverse();
+ => ((Navigation)navigation).FindInverse();
///
/// Gets the entity type that a given navigation property will hold an instance of
@@ -31,7 +31,7 @@ public static IConventionNavigation FindInverse([NotNull] this IConventionNaviga
/// The navigation property to find the target entity type of.
/// The target entity type.
public static IConventionEntityType GetTargetType([NotNull] this IConventionNavigation navigation)
- => (IConventionEntityType)((INavigation)navigation).GetTargetType();
+ => ((Navigation)navigation).GetTargetType();
///
/// Sets a value indicating whether this navigation should be eager loaded by default.
diff --git a/src/EFCore/Extensions/EntityTypeExtensions.cs b/src/EFCore/Extensions/EntityTypeExtensions.cs
index 9fa89af5a72..12e3ee60728 100644
--- a/src/EFCore/Extensions/EntityTypeExtensions.cs
+++ b/src/EFCore/Extensions/EntityTypeExtensions.cs
@@ -397,7 +397,7 @@ public static IKey FindKey([NotNull] this IEntityType entityType, [NotNull] IPro
/// The property to find the foreign keys on.
/// The foreign keys.
public static IEnumerable FindForeignKeys([NotNull] this IEntityType entityType, [NotNull] IProperty property)
- => entityType.FindForeignKeys(new[] { property });
+ => property.GetContainingForeignKeys();
///
/// Gets the foreign keys defined on the given properties. Only foreign keys that are defined on exactly the specified
@@ -413,13 +413,8 @@ public static IEnumerable 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));
}
///
diff --git a/src/EFCore/Extensions/MutableNavigationExtensions.cs b/src/EFCore/Extensions/MutableNavigationExtensions.cs
index 725114b89d9..620ba2a7d74 100644
--- a/src/EFCore/Extensions/MutableNavigationExtensions.cs
+++ b/src/EFCore/Extensions/MutableNavigationExtensions.cs
@@ -22,7 +22,7 @@ public static class MutableNavigationExtensions
/// The inverse navigation, or null if none is defined.
///
public static IMutableNavigation FindInverse([NotNull] this IMutableNavigation navigation)
- => (IMutableNavigation)((INavigation)navigation).FindInverse();
+ => ((Navigation)navigation).FindInverse();
///
/// Gets the entity type that a given navigation property will hold an instance of
@@ -31,7 +31,7 @@ public static IMutableNavigation FindInverse([NotNull] this IMutableNavigation n
/// The navigation property to find the target entity type of.
/// The target entity type.
public static IMutableEntityType GetTargetType([NotNull] this IMutableNavigation navigation)
- => (IMutableEntityType)((INavigation)navigation).GetTargetType();
+ => ((Navigation)navigation).GetTargetType();
///
/// Sets a value indicating whether this navigation should be eager loaded by default.
diff --git a/src/EFCore/Extensions/NavigationExtensions.cs b/src/EFCore/Extensions/NavigationExtensions.cs
index 06b6faf68d9..eba202e4705 100644
--- a/src/EFCore/Extensions/NavigationExtensions.cs
+++ b/src/EFCore/Extensions/NavigationExtensions.cs
@@ -62,13 +62,7 @@ public static bool IsCollection([NotNull] this INavigation navigation)
///
[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();
///
/// Gets the entity type that a given navigation property will hold an instance of
@@ -78,13 +72,7 @@ public static INavigation FindInverse([NotNull] this INavigation navigation)
/// The target entity type.
[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();
///
/// Gets a value indicating whether this navigation should be eager loaded by default.
diff --git a/src/EFCore/Infrastructure/ModelValidator.cs b/src/EFCore/Infrastructure/ModelValidator.cs
index 2ab1d729899..ecc38f320b8 100644
--- a/src/EFCore/Infrastructure/ModelValidator.cs
+++ b/src/EFCore/Infrastructure/ModelValidator.cs
@@ -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()));
+ }
+ }
}
}
@@ -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);
diff --git a/src/EFCore/Metadata/Builders/IConventionSkipNavigationBuilder.cs b/src/EFCore/Metadata/Builders/IConventionSkipNavigationBuilder.cs
new file mode 100644
index 00000000000..97cc68d1e79
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/IConventionSkipNavigationBuilder.cs
@@ -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
+{
+ ///
+ ///
+ /// Provides a simple API surface for configuring an from conventions.
+ ///
+ ///
+ /// This interface is typically used by database providers (and other extensions). It is generally
+ /// not used in application code.
+ ///
+ ///
+ public interface IConventionSkipNavigationBuilder : IConventionAnnotatableBuilder
+ {
+ ///
+ /// The navigation property being configured.
+ ///
+ new IConventionSkipNavigation Metadata { get; }
+ }
+}
diff --git a/src/EFCore/Metadata/Conventions/ConventionSet.cs b/src/EFCore/Metadata/Conventions/ConventionSet.cs
index b296fc09bc5..8219dbe0c9e 100644
--- a/src/EFCore/Metadata/Conventions/ConventionSet.cs
+++ b/src/EFCore/Metadata/Conventions/ConventionSet.cs
@@ -121,11 +121,41 @@ public class ConventionSet
///
public virtual IList NavigationAddedConventions { get; } = new List();
+ ///
+ /// Conventions to run when an annotation is changed on a navigation property.
+ ///
+ public virtual IList NavigationAnnotationChangedConventions { get; }
+ = new List();
+
///
/// Conventions to run when a navigation property is removed.
///
public virtual IList NavigationRemovedConventions { get; } = new List();
+ ///
+ /// Conventions to run when a skip navigation property is added.
+ ///
+ public virtual IList SkipNavigationAddedConventions { get; }
+ = new List();
+
+ ///
+ /// Conventions to run when an annotation is changed on a skip navigation property.
+ ///
+ public virtual IList SkipNavigationAnnotationChangedConventions { get; }
+ = new List();
+
+ ///
+ /// Conventions to run when a skip navigation inverse is changed.
+ ///
+ public virtual IList SkipNavigationInverseChangedConventions { get; }
+ = new List();
+
+ ///
+ /// Conventions to run when a skip navigation property is removed.
+ ///
+ public virtual IList SkipNavigationRemovedConventions { get; }
+ = new List();
+
///
/// Conventions to run when a key is added.
///
@@ -187,6 +217,11 @@ public class ConventionSet
public virtual IList PropertyAnnotationChangedConventions { get; }
= new List();
+ ///
+ /// Conventions to run when a property is removed.
+ ///
+ public virtual IList PropertyRemovedConventions { get; } = new List();
+
///
/// Replaces an existing convention with a derived convention.
///
diff --git a/src/EFCore/Metadata/Conventions/INavigationAnnotationChangedConvention.cs b/src/EFCore/Metadata/Conventions/INavigationAnnotationChangedConvention.cs
new file mode 100644
index 00000000000..8497930f5a9
--- /dev/null
+++ b/src/EFCore/Metadata/Conventions/INavigationAnnotationChangedConvention.cs
@@ -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
+{
+ ///
+ /// Represents an operation that should be performed when an annotation is changed on a navigation.
+ ///
+ public interface INavigationAnnotationChangedConvention : IConvention
+ {
+ ///
+ /// Called after an annotation is changed on a navigation.
+ ///
+ /// The builder for the foreign key.
+ /// The navigation.
+ /// The annotation name.
+ /// The new annotation.
+ /// The old annotation.
+ /// Additional information associated with convention execution.
+ void ProcessNavigationAnnotationChanged(
+ [NotNull] IConventionRelationshipBuilder relationshipBuilder,
+ [NotNull] IConventionNavigation navigation,
+ [NotNull] string name,
+ [CanBeNull] IConventionAnnotation annotation,
+ [CanBeNull] IConventionAnnotation oldAnnotation,
+ [NotNull] IConventionContext context);
+ }
+}
diff --git a/src/EFCore/Metadata/Conventions/IPropertyRemovedConvention.cs b/src/EFCore/Metadata/Conventions/IPropertyRemovedConvention.cs
new file mode 100644
index 00000000000..06efe5c2de7
--- /dev/null
+++ b/src/EFCore/Metadata/Conventions/IPropertyRemovedConvention.cs
@@ -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
+{
+ ///
+ /// Represents an operation that should be performed when a property is removed from the entity type.
+ ///
+ public interface IPropertyRemovedConvention : IConvention
+ {
+ ///
+ /// Called after a property is removed from the entity type.
+ ///
+ /// The builder for the entity type that contained the property.
+ /// The removed property.
+ /// Additional information associated with convention execution.
+ void ProcessPropertyRemoved(
+ [NotNull] IConventionEntityTypeBuilder entityTypeBuilder,
+ [NotNull] IConventionProperty property,
+ [NotNull] IConventionContext context);
+ }
+}
diff --git a/src/EFCore/Metadata/Conventions/ISkipNavigationAddedConvention.cs b/src/EFCore/Metadata/Conventions/ISkipNavigationAddedConvention.cs
new file mode 100644
index 00000000000..b22b563c227
--- /dev/null
+++ b/src/EFCore/Metadata/Conventions/ISkipNavigationAddedConvention.cs
@@ -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
+{
+ ///
+ /// Represents an operation that should be performed when a skip navigation is added to the entity type.
+ ///
+ public interface ISkipNavigationAddedConvention : IConvention
+ {
+ ///
+ /// Called after a skip navigation is added to the entity type.
+ ///
+ /// The builder for the skip navigation.
+ /// Additional information associated with convention execution.
+ void ProcessSkipNavigationAdded(
+ [NotNull] IConventionSkipNavigationBuilder skipNavigationBuilder,
+ [NotNull] IConventionContext context);
+ }
+}
diff --git a/src/EFCore/Metadata/Conventions/ISkipNavigationAnnotationChangedConvention.cs b/src/EFCore/Metadata/Conventions/ISkipNavigationAnnotationChangedConvention.cs
new file mode 100644
index 00000000000..90c701529ae
--- /dev/null
+++ b/src/EFCore/Metadata/Conventions/ISkipNavigationAnnotationChangedConvention.cs
@@ -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
+{
+ ///
+ /// Represents an operation that should be performed when an annotation is changed on a skip navigation.
+ ///
+ public interface ISkipNavigationAnnotationChangedConvention : IConvention
+ {
+ ///
+ /// Called after an annotation is changed on a skip navigation.
+ ///
+ /// The builder for the skip navigation.
+ /// The annotation name.
+ /// The new annotation.
+ /// The old annotation.
+ /// Additional information associated with convention execution.
+ void ProcessSkipNavigationAnnotationChanged(
+ [NotNull] IConventionSkipNavigationBuilder skipNavigationBuilder,
+ [NotNull] string name,
+ [CanBeNull] IConventionAnnotation annotation,
+ [CanBeNull] IConventionAnnotation oldAnnotation,
+ [NotNull] IConventionContext context);
+ }
+}
diff --git a/src/EFCore/Metadata/Conventions/ISkipNavigationInverseChangedConvention.cs b/src/EFCore/Metadata/Conventions/ISkipNavigationInverseChangedConvention.cs
new file mode 100644
index 00000000000..24697101879
--- /dev/null
+++ b/src/EFCore/Metadata/Conventions/ISkipNavigationInverseChangedConvention.cs
@@ -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
+{
+ ///
+ /// Represents an operation that should be performed when a skip navigation inverse is changed.
+ ///
+ public interface ISkipNavigationInverseChangedConvention : IConvention
+ {
+ ///
+ /// Called after a skip navigation inverse is changed.
+ ///
+ /// The builder for the skip navigation.
+ /// The current inverse skip navigation.
+ /// The old inverse skip navigation.
+ /// Additional information associated with convention execution.
+ void ProcessSkipNavigationInverseChanged(
+ [NotNull] IConventionSkipNavigationBuilder skipNavigationBuilder,
+ [NotNull] IConventionSkipNavigation inverse,
+ [NotNull] IConventionSkipNavigation oldInverse,
+ [NotNull] IConventionContext context);
+ }
+}
diff --git a/src/EFCore/Metadata/Conventions/ISkipNavigationRemovedConvention.cs b/src/EFCore/Metadata/Conventions/ISkipNavigationRemovedConvention.cs
new file mode 100644
index 00000000000..850772fa88e
--- /dev/null
+++ b/src/EFCore/Metadata/Conventions/ISkipNavigationRemovedConvention.cs
@@ -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
+{
+ ///
+ /// Represents an operation that should be performed when a skip navigation is removed from the entity type.
+ ///
+ public interface ISkipNavigationRemovedConvention : IConvention
+ {
+ ///
+ /// Called after a skip navigation is removed from the entity type.
+ ///
+ /// The builder for the entity type that contained the navigation.
+ /// The removed navigation.
+ /// Additional information associated with convention execution.
+ void ProcessSkipNavigationRemoved(
+ [NotNull] IConventionEntityTypeBuilder entityTypeBuilder,
+ [NotNull] IConventionSkipNavigation navigation,
+ [NotNull] IConventionContext context);
+ }
+}
diff --git a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ConventionScope.cs b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ConventionScope.cs
index 650f9a9a0a3..8d1be5be210 100644
--- a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ConventionScope.cs
+++ b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ConventionScope.cs
@@ -141,6 +141,31 @@ public abstract string OnNavigationRemoved(
[NotNull] string navigationName,
[CanBeNull] MemberInfo memberInfo);
+ public abstract IConventionAnnotation OnNavigationAnnotationChanged(
+ [NotNull] IConventionRelationshipBuilder relationshipBuilder,
+ [NotNull] IConventionNavigation navigation,
+ [NotNull] string name,
+ [CanBeNull] IConventionAnnotation annotation,
+ [CanBeNull] IConventionAnnotation oldAnnotation);
+
+ public abstract IConventionSkipNavigationBuilder OnSkipNavigationAdded(
+ [NotNull] IConventionSkipNavigationBuilder navigationBuilder);
+
+ public abstract IConventionAnnotation OnSkipNavigationAnnotationChanged(
+ [NotNull] IConventionSkipNavigationBuilder navigationBuilder,
+ [NotNull] string name,
+ [CanBeNull] IConventionAnnotation annotation,
+ [CanBeNull] IConventionAnnotation oldAnnotation);
+
+ public abstract IConventionSkipNavigation OnSkipNavigationInverseChanged(
+ [NotNull] IConventionSkipNavigationBuilder navigationBuilder,
+ [NotNull] IConventionSkipNavigation inverse,
+ [NotNull] IConventionSkipNavigation oldInverse);
+
+ public abstract IConventionSkipNavigation OnSkipNavigationRemoved(
+ [NotNull] IConventionEntityTypeBuilder entityTypeBuilder,
+ [NotNull] IConventionSkipNavigation navigation);
+
public abstract IConventionPropertyBuilder OnPropertyAdded([NotNull] IConventionPropertyBuilder propertyBuilder);
public abstract IConventionAnnotation OnPropertyAnnotationChanged(
@@ -153,6 +178,8 @@ public abstract FieldInfo OnPropertyFieldChanged(
[NotNull] IConventionPropertyBuilder propertyBuilder, FieldInfo newFieldInfo, [CanBeNull] FieldInfo oldFieldInfo);
public abstract IConventionPropertyBuilder OnPropertyNullableChanged([NotNull] IConventionPropertyBuilder propertyBuilder);
+
+ public abstract IConventionProperty OnPropertyRemoved([NotNull] IConventionEntityTypeBuilder entityTypeBuilder, [NotNull] IConventionProperty property);
}
}
}
diff --git a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.DelayedConventionScope.cs b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.DelayedConventionScope.cs
index 0f21202c421..aa374e6fca7 100644
--- a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.DelayedConventionScope.cs
+++ b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.DelayedConventionScope.cs
@@ -199,6 +199,17 @@ public override IConventionNavigation OnNavigationAdded(
return navigation;
}
+ public override IConventionAnnotation OnNavigationAnnotationChanged(
+ IConventionRelationshipBuilder relationshipBuilder,
+ IConventionNavigation navigation,
+ string name,
+ IConventionAnnotation annotation,
+ IConventionAnnotation oldAnnotation)
+ {
+ Add(new OnNavigationAnnotationChangedNode(relationshipBuilder, navigation, name, annotation, oldAnnotation));
+ return annotation;
+ }
+
public override string OnNavigationRemoved(
IConventionEntityTypeBuilder sourceEntityTypeBuilder,
IConventionEntityTypeBuilder targetEntityTypeBuilder,
@@ -209,6 +220,40 @@ public override string OnNavigationRemoved(
return navigationName;
}
+ public override IConventionSkipNavigationBuilder OnSkipNavigationAdded(
+ IConventionSkipNavigationBuilder navigationBuilder)
+ {
+ Add(new OnSkipNavigationAddedNode(navigationBuilder));
+ return navigationBuilder;
+ }
+
+ public override IConventionAnnotation OnSkipNavigationAnnotationChanged(
+ IConventionSkipNavigationBuilder navigationBuilder,
+ string name,
+ IConventionAnnotation annotation,
+ IConventionAnnotation oldAnnotation)
+ {
+ Add(new OnSkipNavigationAnnotationChangedNode(navigationBuilder, name, annotation, oldAnnotation));
+ return annotation;
+ }
+
+ public override IConventionSkipNavigation OnSkipNavigationInverseChanged(
+ IConventionSkipNavigationBuilder navigationBuilder,
+ IConventionSkipNavigation inverse,
+ IConventionSkipNavigation oldInverse)
+ {
+ Add(new OnSkipNavigationInverseChangedNode(navigationBuilder, inverse, oldInverse));
+ return inverse;
+ }
+
+ public override IConventionSkipNavigation OnSkipNavigationRemoved(
+ IConventionEntityTypeBuilder entityTypeBuilder,
+ IConventionSkipNavigation navigation)
+ {
+ Add(new OnSkipNavigationRemovedNode(entityTypeBuilder, navigation));
+ return navigation;
+ }
+
public override IConventionRelationshipBuilder OnForeignKeyPropertiesChanged(
IConventionRelationshipBuilder relationshipBuilder,
IReadOnlyList oldDependentProperties,
@@ -274,6 +319,14 @@ public override IConventionAnnotation OnPropertyAnnotationChanged(
Add(new OnPropertyAnnotationChangedNode(propertyBuilder, name, annotation, oldAnnotation));
return annotation;
}
+
+ public override IConventionProperty OnPropertyRemoved(
+ IConventionEntityTypeBuilder entityTypeBuilder,
+ IConventionProperty property)
+ {
+ Add(new OnPropertyRemovedNode(entityTypeBuilder, property));
+ return property;
+ }
}
private sealed class OnModelAnnotationChangedNode : ConventionNode
@@ -545,6 +598,33 @@ public override void Run(ConventionDispatcher dispatcher)
=> dispatcher._immediateConventionScope.OnNavigationAdded(RelationshipBuilder, Navigation);
}
+ private sealed class OnNavigationAnnotationChangedNode : ConventionNode
+ {
+ public OnNavigationAnnotationChangedNode(
+ IConventionRelationshipBuilder relationshipBuilder,
+ IConventionNavigation navigation,
+ string name,
+ IConventionAnnotation annotation,
+ IConventionAnnotation oldAnnotation)
+ {
+ RelationshipBuilder = relationshipBuilder;
+ Navigation = navigation;
+ Name = name;
+ Annotation = annotation;
+ OldAnnotation = oldAnnotation;
+ }
+
+ public IConventionRelationshipBuilder RelationshipBuilder { get; }
+ public IConventionNavigation Navigation { get; }
+ public string Name { get; }
+ public IConventionAnnotation Annotation { get; }
+ public IConventionAnnotation OldAnnotation { get; }
+
+ public override void Run(ConventionDispatcher dispatcher)
+ => dispatcher._immediateConventionScope.OnNavigationAnnotationChanged(
+ RelationshipBuilder, Navigation, Name, Annotation, OldAnnotation);
+ }
+
private sealed class OnNavigationRemovedNode : ConventionNode
{
public OnNavigationRemovedNode(
@@ -569,6 +649,80 @@ public override void Run(ConventionDispatcher dispatcher)
SourceEntityTypeBuilder, TargetEntityTypeBuilder, NavigationName, MemberInfo);
}
+ private sealed class OnSkipNavigationAddedNode : ConventionNode
+ {
+ public OnSkipNavigationAddedNode(IConventionSkipNavigationBuilder navigationBuilder)
+ {
+ NavigationBuilder = navigationBuilder;
+ }
+
+ public IConventionSkipNavigationBuilder NavigationBuilder { get; }
+
+ public override void Run(ConventionDispatcher dispatcher)
+ => dispatcher._immediateConventionScope.OnSkipNavigationAdded(NavigationBuilder);
+ }
+
+ private sealed class OnSkipNavigationAnnotationChangedNode : ConventionNode
+ {
+ public OnSkipNavigationAnnotationChangedNode(
+ IConventionSkipNavigationBuilder navigationBuilder,
+ string name,
+ IConventionAnnotation annotation,
+ IConventionAnnotation oldAnnotation)
+ {
+ NavigationBuilder = navigationBuilder;
+ Name = name;
+ Annotation = annotation;
+ OldAnnotation = oldAnnotation;
+ }
+
+ public IConventionSkipNavigationBuilder NavigationBuilder { get; }
+ public string Name { get; }
+ public IConventionAnnotation Annotation { get; }
+ public IConventionAnnotation OldAnnotation { get; }
+
+ public override void Run(ConventionDispatcher dispatcher)
+ => dispatcher._immediateConventionScope.OnSkipNavigationAnnotationChanged(
+ NavigationBuilder, Name, Annotation, OldAnnotation);
+ }
+
+ private sealed class OnSkipNavigationInverseChangedNode : ConventionNode
+ {
+ public OnSkipNavigationInverseChangedNode(
+ IConventionSkipNavigationBuilder navigationBuilder,
+ IConventionSkipNavigation inverse,
+ IConventionSkipNavigation oldInverse)
+ {
+ NavigationBuilder = navigationBuilder;
+ Inverse = inverse;
+ OldInverse = oldInverse;
+ }
+
+ public IConventionSkipNavigationBuilder NavigationBuilder { get; }
+ public IConventionSkipNavigation Inverse { get; }
+ public IConventionSkipNavigation OldInverse { get; }
+
+ public override void Run(ConventionDispatcher dispatcher)
+ => dispatcher._immediateConventionScope.OnSkipNavigationInverseChanged(NavigationBuilder, Inverse, OldInverse);
+ }
+
+ private sealed class OnSkipNavigationRemovedNode : ConventionNode
+ {
+ public OnSkipNavigationRemovedNode(
+ IConventionEntityTypeBuilder entityTypeBuilder,
+ IConventionSkipNavigation navigation)
+ {
+ EntityTypeBuilder = entityTypeBuilder;
+ Navigation = navigation;
+ }
+
+ public IConventionEntityTypeBuilder EntityTypeBuilder { get; }
+ public IConventionSkipNavigation Navigation { get; }
+
+ public override void Run(ConventionDispatcher dispatcher)
+ => dispatcher._immediateConventionScope.OnSkipNavigationRemoved(EntityTypeBuilder, Navigation);
+ }
+
private sealed class OnKeyAddedNode : ConventionNode
{
public OnKeyAddedNode(IConventionKeyBuilder keyBuilder)
@@ -768,5 +922,22 @@ public override void Run(ConventionDispatcher dispatcher)
=> dispatcher._immediateConventionScope.OnPropertyAnnotationChanged(
PropertyBuilder, Name, Annotation, OldAnnotation);
}
+
+ private sealed class OnPropertyRemovedNode : ConventionNode
+ {
+ public OnPropertyRemovedNode(
+ IConventionEntityTypeBuilder entityTypeBuilder,
+ IConventionProperty property)
+ {
+ EntityTypeBuilder = entityTypeBuilder;
+ Property = property;
+ }
+
+ public IConventionEntityTypeBuilder EntityTypeBuilder { get; }
+ public IConventionProperty Property { get; }
+
+ public override void Run(ConventionDispatcher dispatcher)
+ => dispatcher._immediateConventionScope.OnPropertyRemoved(EntityTypeBuilder, Property);
+ }
}
}
diff --git a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs
index e9514bce63b..156e63bc02d 100644
--- a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs
+++ b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs
@@ -6,6 +6,7 @@
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Microsoft.EntityFrameworkCore.Utilities;
namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal
{
@@ -19,12 +20,15 @@ private sealed class ImmediateConventionScope : ConventionScope
private readonly ConventionContext _entityTypeConventionContext;
private readonly ConventionContext _relationshipBuilderConventionContext;
private readonly ConventionContext _foreignKeyConventionContext;
+ private readonly ConventionContext _skipNavigationBuilderConventionContext;
+ private readonly ConventionContext _skipNavigationConventionContext;
private readonly ConventionContext _navigationConventionContext;
private readonly ConventionContext _indexBuilderConventionContext;
private readonly ConventionContext _indexConventionContext;
private readonly ConventionContext _keyBuilderConventionContext;
private readonly ConventionContext _keyConventionContext;
private readonly ConventionContext _propertyBuilderConventionContext;
+ private readonly ConventionContext _propertyConventionContext;
private readonly ConventionContext _modelBuilderConventionContext;
private readonly ConventionContext _annotationConventionContext;
private readonly ConventionContext _stringConventionContext;
@@ -38,12 +42,15 @@ public ImmediateConventionScope([NotNull] ConventionSet conventionSet, Conventio
_entityTypeConventionContext = new ConventionContext(dispatcher);
_relationshipBuilderConventionContext = new ConventionContext(dispatcher);
_foreignKeyConventionContext = new ConventionContext(dispatcher);
+ _skipNavigationBuilderConventionContext = new ConventionContext(dispatcher);
+ _skipNavigationConventionContext = new ConventionContext(dispatcher);
_navigationConventionContext = new ConventionContext(dispatcher);
_indexBuilderConventionContext = new ConventionContext(dispatcher);
_indexConventionContext = new ConventionContext(dispatcher);
_keyBuilderConventionContext = new ConventionContext(dispatcher);
_keyConventionContext = new ConventionContext(dispatcher);
_propertyBuilderConventionContext = new ConventionContext(dispatcher);
+ _propertyConventionContext = new ConventionContext(dispatcher);
_modelBuilderConventionContext = new ConventionContext(dispatcher);
_annotationConventionContext = new ConventionContext(dispatcher);
_stringConventionContext = new ConventionContext(dispatcher);
@@ -600,6 +607,36 @@ public override IConventionNavigation OnNavigationAdded(
return navigation;
}
+ public override IConventionAnnotation OnNavigationAnnotationChanged(
+ IConventionRelationshipBuilder relationshipBuilder,
+ IConventionNavigation navigation,
+ string name,
+ IConventionAnnotation annotation,
+ IConventionAnnotation oldAnnotation)
+ {
+ if (relationshipBuilder.Metadata.Builder == null
+ || relationshipBuilder.Metadata.GetNavigation(navigation.IsDependentToPrincipal()) != navigation)
+ {
+ return null;
+ }
+
+ using (_dispatcher.DelayConventions())
+ {
+ _annotationConventionContext.ResetState(annotation);
+ foreach (var navigationConvention in _conventionSet.NavigationAnnotationChangedConventions)
+ {
+ navigationConvention.ProcessNavigationAnnotationChanged(
+ relationshipBuilder, navigation, name, annotation, oldAnnotation, _annotationConventionContext);
+ if (_annotationConventionContext.ShouldStopProcessing())
+ {
+ return _annotationConventionContext.Result;
+ }
+ }
+ }
+
+ return annotation;
+ }
+
public override string OnNavigationRemoved(
IConventionEntityTypeBuilder sourceEntityTypeBuilder,
IConventionEntityTypeBuilder targetEntityTypeBuilder,
@@ -639,6 +676,140 @@ public override string OnNavigationRemoved(
return navigationName;
}
+ public override IConventionSkipNavigationBuilder OnSkipNavigationAdded(
+ IConventionSkipNavigationBuilder navigationBuilder)
+ {
+ if (navigationBuilder.Metadata.DeclaringEntityType.Builder == null)
+ {
+ return null;
+ }
+
+ using (_dispatcher.DelayConventions())
+ {
+ _skipNavigationBuilderConventionContext.ResetState(navigationBuilder);
+ foreach (var skipNavigationConvention in _conventionSet.SkipNavigationAddedConventions)
+ {
+ if (navigationBuilder.Metadata.Builder == null)
+ {
+ Check.DebugAssert(false, "null builder");
+ return null;
+ }
+
+ skipNavigationConvention.ProcessSkipNavigationAdded(navigationBuilder, _skipNavigationBuilderConventionContext);
+ if (_skipNavigationBuilderConventionContext.ShouldStopProcessing())
+ {
+ return _skipNavigationBuilderConventionContext.Result;
+ }
+ }
+ }
+
+ if (navigationBuilder.Metadata.Builder == null)
+ {
+ return null;
+ }
+
+ return navigationBuilder;
+ }
+
+ public override IConventionAnnotation OnSkipNavigationAnnotationChanged(
+ IConventionSkipNavigationBuilder navigationBuilder,
+ string name,
+ IConventionAnnotation annotation,
+ IConventionAnnotation oldAnnotation)
+ {
+ if (navigationBuilder.Metadata.Builder == null)
+ {
+ return null;
+ }
+
+ using (_dispatcher.DelayConventions())
+ {
+ _annotationConventionContext.ResetState(annotation);
+ foreach (var skipNavigationConvention in _conventionSet.SkipNavigationAnnotationChangedConventions)
+ {
+ if (navigationBuilder.Metadata.Builder != null
+ && navigationBuilder.Metadata.FindAnnotation(name) != annotation)
+ {
+ Check.DebugAssert(false, "annotation removed");
+ return null;
+ }
+
+ skipNavigationConvention.ProcessSkipNavigationAnnotationChanged(
+ navigationBuilder, name, annotation, oldAnnotation, _annotationConventionContext);
+ if (_annotationConventionContext.ShouldStopProcessing())
+ {
+ return _annotationConventionContext.Result;
+ }
+ }
+ }
+
+ return annotation;
+ }
+
+ public override IConventionSkipNavigation OnSkipNavigationInverseChanged(
+ IConventionSkipNavigationBuilder navigationBuilder,
+ IConventionSkipNavigation inverse,
+ IConventionSkipNavigation oldInverse)
+ {
+ if (navigationBuilder.Metadata.DeclaringEntityType.Builder == null)
+ {
+ return null;
+ }
+
+ using (_dispatcher.DelayConventions())
+ {
+ _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())
+ {
+ return _skipNavigationConventionContext.Result;
+ }
+ }
+ }
+
+ if (navigationBuilder.Metadata.Builder == null)
+ {
+ return null;
+ }
+
+ return inverse;
+ }
+
+ public override IConventionSkipNavigation OnSkipNavigationRemoved(
+ IConventionEntityTypeBuilder entityTypeBuilder,
+ IConventionSkipNavigation navigation)
+ {
+ if (entityTypeBuilder.Metadata.Builder == null)
+ {
+ return null;
+ }
+
+ using (_dispatcher.DelayConventions())
+ {
+ _skipNavigationConventionContext.ResetState(navigation);
+ foreach (var skipNavigationConvention in _conventionSet.SkipNavigationRemovedConventions)
+ {
+ skipNavigationConvention.ProcessSkipNavigationRemoved(entityTypeBuilder, navigation, _skipNavigationConventionContext);
+ if (_skipNavigationConventionContext.ShouldStopProcessing())
+ {
+ return _skipNavigationConventionContext.Result;
+ }
+ }
+ }
+
+ return navigation;
+ }
+
public override IConventionKeyBuilder OnKeyAdded(IConventionKeyBuilder keyBuilder)
{
if (keyBuilder.Metadata.DeclaringEntityType.Builder == null)
@@ -953,6 +1124,31 @@ public override IConventionAnnotation OnPropertyAnnotationChanged(
return annotation;
}
+
+ public override IConventionProperty OnPropertyRemoved(
+ IConventionEntityTypeBuilder entityTypeBuilder,
+ IConventionProperty property)
+ {
+ if (entityTypeBuilder.Metadata.Builder == null)
+ {
+ return null;
+ }
+
+ using (_dispatcher.DelayConventions())
+ {
+ _propertyConventionContext.ResetState(property);
+ foreach (var propertyConvention in _conventionSet.PropertyRemovedConventions)
+ {
+ propertyConvention.ProcessPropertyRemoved(entityTypeBuilder, property, _propertyConventionContext);
+ if (_propertyConventionContext.ShouldStopProcessing())
+ {
+ return _propertyConventionContext.Result;
+ }
+ }
+ }
+
+ return property;
+ }
}
}
}
diff --git a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs
index 6e0fcd79301..dad11083014 100644
--- a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs
+++ b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs
@@ -287,6 +287,89 @@ public virtual string OnNavigationRemoved(
navigationName,
memberInfo);
+ ///
+ /// 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.
+ ///
+ public virtual IConventionAnnotation OnNavigationAnnotationChanged(
+ [NotNull] IConventionRelationshipBuilder relationshipBuilder,
+ [NotNull] IConventionNavigation navigation,
+ [NotNull] string name,
+ [CanBeNull] IConventionAnnotation annotation,
+ [CanBeNull] IConventionAnnotation oldAnnotation)
+ {
+ if (CoreAnnotationNames.AllNames.Contains(name))
+ {
+ return annotation;
+ }
+
+ return _scope.OnNavigationAnnotationChanged(
+ relationshipBuilder,
+ navigation,
+ name,
+ annotation,
+ oldAnnotation);
+ }
+
+ ///
+ /// 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.
+ ///
+ public virtual IConventionSkipNavigationBuilder OnSkipNavigationAdded(
+ [NotNull] IConventionSkipNavigationBuilder navigationBuilder)
+ => _scope.OnSkipNavigationAdded(navigationBuilder);
+
+ ///
+ /// 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.
+ ///
+ public virtual IConventionSkipNavigation OnSkipNavigationInverseChanged(
+ [NotNull] IConventionSkipNavigationBuilder navigationBuilder,
+ [NotNull] IConventionSkipNavigation inverse,
+ [NotNull] IConventionSkipNavigation oldInverse)
+ => _scope.OnSkipNavigationInverseChanged(navigationBuilder, inverse, oldInverse);
+
+ ///
+ /// 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.
+ ///
+ public virtual IConventionSkipNavigation OnSkipNavigationRemoved(
+ [NotNull] IConventionEntityTypeBuilder entityTypeBuilder,
+ [NotNull] IConventionSkipNavigation navigation)
+ => _scope.OnSkipNavigationRemoved(entityTypeBuilder, navigation);
+
+ ///
+ /// 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.
+ ///
+ public virtual IConventionAnnotation OnSkipNavigationAnnotationChanged(
+ [NotNull] IConventionSkipNavigationBuilder navigationBuilder,
+ [NotNull] string name,
+ [CanBeNull] IConventionAnnotation annotation,
+ [CanBeNull] IConventionAnnotation oldAnnotation)
+ {
+ if (CoreAnnotationNames.AllNames.Contains(name))
+ {
+ return annotation;
+ }
+
+ return _scope.OnSkipNavigationAnnotationChanged(
+ navigationBuilder,
+ name,
+ annotation,
+ oldAnnotation);
+ }
+
///
/// 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
@@ -402,6 +485,17 @@ public virtual IConventionAnnotation OnIndexAnnotationChanged(
public virtual IConventionPropertyBuilder OnPropertyAdded([NotNull] IConventionPropertyBuilder propertyBuilder)
=> _scope.OnPropertyAdded(propertyBuilder);
+ ///
+ /// 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.
+ ///
+ public virtual IConventionProperty OnPropertyRemoved(
+ [NotNull] IConventionEntityTypeBuilder entityTypeBuilder,
+ [NotNull] IConventionProperty property)
+ => _scope.OnPropertyRemoved(entityTypeBuilder, property);
+
///
/// 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
diff --git a/src/EFCore/Metadata/IConventionEntityType.cs b/src/EFCore/Metadata/IConventionEntityType.cs
index 7c8f0e4ac34..87332cceb39 100644
--- a/src/EFCore/Metadata/IConventionEntityType.cs
+++ b/src/EFCore/Metadata/IConventionEntityType.cs
@@ -3,9 +3,11 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Microsoft.EntityFrameworkCore.Utilities;
namespace Microsoft.EntityFrameworkCore.Metadata
{
@@ -167,6 +169,85 @@ IConventionForeignKey AddForeignKey(
/// The foreign key to be removed.
void RemoveForeignKey([NotNull] IConventionForeignKey foreignKey);
+ ///
+ /// Adds a new skip navigation properties to this entity type.
+ ///
+ /// The name of the skip navigation property to add.
+ ///
+ ///
+ /// The corresponding CLR type member or null for a shadow property.
+ ///
+ ///
+ /// An indexer with a string parameter and object return type can be used.
+ ///
+ ///
+ /// The entity type that the skip navigation property will hold an instance(s) of.
+ /// The foreign key to the association type.
+ /// Whether the navigation property is a collection property.
+ ///
+ /// Whether the navigation property is defined on the principal side of the underlying foreign key.
+ ///
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// The newly created skip navigation property.
+ IConventionSkipNavigation AddSkipNavigation(
+ [NotNull] string name,
+ [CanBeNull] MemberInfo memberInfo,
+ [NotNull] IConventionEntityType targetEntityType,
+ [NotNull] IConventionForeignKey foreignKey,
+ bool collection,
+ bool onPrincipal,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Gets a skip navigation property on this entity type. Returns null if no navigation property is found.
+ ///
+ /// The navigation property on the entity class.
+ /// The navigation property, or null if none is found.
+ new IConventionSkipNavigation FindSkipNavigation([NotNull] MemberInfo memberInfo)
+ => (IConventionSkipNavigation)((IEntityType)this).FindSkipNavigation(memberInfo);
+
+ ///
+ /// Gets a skip navigation property on this entity type. Returns null if no skip navigation property is found.
+ ///
+ /// The name of the navigation property on the entity class.
+ /// The navigation property, or null if none is found.
+ new IConventionSkipNavigation FindSkipNavigation([NotNull] string name);
+
+ ///
+ /// Gets a skip navigation property on this entity type. Does not return skip navigation properties defined on a base type.
+ /// Returns null if no skip navigation property is found.
+ ///
+ /// The name of the navigation property on the entity class.
+ /// The navigation property, or null if none is found.
+ new IConventionSkipNavigation FindDeclaredSkipNavigation([NotNull] string name)
+ => (IConventionSkipNavigation)((IEntityType)this).FindDeclaredSkipNavigation(name);
+
+ ///
+ ///
+ /// Gets all skip navigation properties declared on this entity type.
+ ///
+ ///
+ /// This method does not return skip navigation properties declared declared on base types.
+ /// It is useful when iterating over all entity types to avoid processing the same foreign key more than once.
+ /// Use to also return skip navigation properties declared on base types.
+ ///
+ ///
+ /// Declared foreign keys.
+ new IEnumerable GetDeclaredSkipNavigations()
+ => ((IEntityType)this).GetDeclaredSkipNavigations().Cast();
+
+ ///
+ /// Gets all skip navigation properties on this entity type.
+ ///
+ /// All skip navigation properties on this entity type.
+ new IEnumerable GetSkipNavigations();
+
+ ///
+ /// Removes a skip navigation property from this entity type.
+ ///
+ /// The skip navigation to be removed.
+ void RemoveSkipNavigation([NotNull] IConventionSkipNavigation navigation);
+
///
/// Adds an index to this entity type.
///
diff --git a/src/EFCore/Metadata/IConventionForeignKey.cs b/src/EFCore/Metadata/IConventionForeignKey.cs
index be65d4e9520..048e006fdbe 100644
--- a/src/EFCore/Metadata/IConventionForeignKey.cs
+++ b/src/EFCore/Metadata/IConventionForeignKey.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
@@ -207,5 +208,12 @@ void SetProperties(
///
/// The configuration source for .
ConfigurationSource? GetPrincipalToDependentConfigurationSource();
+
+ ///
+ /// Gets all skip navigations using this foreign key.
+ ///
+ /// The skip navigations using this foreign key.
+ new IEnumerable GetReferencingSkipNavigations()
+ => ((IForeignKey)this).GetReferencingSkipNavigations().Cast();
}
}
diff --git a/src/EFCore/Metadata/IConventionNavigation.cs b/src/EFCore/Metadata/IConventionNavigation.cs
index 99b9339321c..b666ba8f88e 100644
--- a/src/EFCore/Metadata/IConventionNavigation.cs
+++ b/src/EFCore/Metadata/IConventionNavigation.cs
@@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata
public interface IConventionNavigation : INavigation, IConventionPropertyBase
{
///
- /// Gets the type that this property belongs to.
+ /// Gets the type that this navigation property belongs to.
///
new IConventionEntityType DeclaringEntityType { get; }
diff --git a/src/EFCore/Metadata/IConventionSkipNavigation.cs b/src/EFCore/Metadata/IConventionSkipNavigation.cs
new file mode 100644
index 00000000000..4edba214618
--- /dev/null
+++ b/src/EFCore/Metadata/IConventionSkipNavigation.cs
@@ -0,0 +1,72 @@
+// 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
+{
+ ///
+ ///
+ /// Represents a navigation property that is part of a relationship
+ /// that is forwarded through a third entity type.
+ ///
+ ///
+ /// This interface is used during model creation and allows the metadata to be modified.
+ /// Once the model is built, represents a read-only view of the same metadata.
+ ///
+ ///
+ public interface IConventionSkipNavigation : ISkipNavigation, IConventionPropertyBase
+ {
+ ///
+ /// Gets the builder that can be used to configure this property.
+ ///
+ IConventionSkipNavigationBuilder Builder { get; }
+
+ ///
+ /// Gets the type that this navigation property belongs to.
+ ///
+ new IConventionEntityType DeclaringEntityType => IsOnPrincipal ? ForeignKey.PrincipalEntityType : ForeignKey.DeclaringEntityType;
+
+ ///
+ /// Gets the association type used by the foreign key.
+ ///
+ new IConventionEntityType AssociationEntityType => IsOnPrincipal ? ForeignKey.DeclaringEntityType : ForeignKey.PrincipalEntityType;
+
+ ///
+ /// Gets the entity type that this navigation property will hold an instance(s) of.
+ ///
+ new IConventionEntityType TargetEntityType { get; }
+
+ ///
+ /// Gets the foreign key to the association type.
+ ///
+ new IConventionForeignKey ForeignKey { get; }
+
+ ///
+ /// Gets the inverse skip navigation.
+ ///
+ new IConventionSkipNavigation Inverse { get; }
+
+ ///
+ /// Returns the configuration source for this property.
+ ///
+ /// The configuration source.
+ ConfigurationSource GetConfigurationSource();
+
+ ///
+ /// Sets the inverse skip navigation.
+ ///
+ ///
+ /// The inverse skip navigation. Passing null will result in there being no inverse navigation property defined.
+ ///
+ /// Indicates whether the configuration was specified using a data annotation.
+ IConventionSkipNavigation SetInverse([CanBeNull] IConventionSkipNavigation inverse, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns the configuration source for .
+ ///
+ /// The configuration source for .
+ ConfigurationSource? GetInverseConfigurationSource();
+ }
+}
diff --git a/src/EFCore/Metadata/IEntityType.cs b/src/EFCore/Metadata/IEntityType.cs
index 3ec6aacf054..64cf67caeab 100644
--- a/src/EFCore/Metadata/IEntityType.cs
+++ b/src/EFCore/Metadata/IEntityType.cs
@@ -2,7 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Utilities;
namespace Microsoft.EntityFrameworkCore.Metadata
{
@@ -69,6 +72,58 @@ IForeignKey FindForeignKey(
/// The foreign keys defined on this entity type.
IEnumerable GetForeignKeys();
+ ///
+ /// Gets a skip navigation property on this entity type. Returns null if no navigation property is found.
+ ///
+ /// The navigation property on the entity class.
+ /// The navigation property, or null if none is found.
+ ISkipNavigation FindSkipNavigation([NotNull] MemberInfo memberInfo)
+ => FindSkipNavigation(Check.NotNull(memberInfo, nameof(memberInfo)).GetSimpleMemberName());
+
+ ///
+ /// Gets a skip navigation property on this entity type. Returns null if no skip navigation property is found.
+ ///
+ /// The name of the navigation property on the entity class.
+ /// The navigation property, or null if none is found.
+ ISkipNavigation FindSkipNavigation([NotNull] string name);
+
+ ///
+ ///
+ /// Gets a skip navigation property on this entity type.
+ ///
+ ///
+ /// Does not return skip navigation properties defined on a base type.
+ /// Returns null if no skip navigation property is found.
+ ///
+ ///
+ /// The name of the navigation property on the entity class.
+ /// The navigation property, or null if none is found.
+ ISkipNavigation FindDeclaredSkipNavigation([NotNull] string name)
+ {
+ var navigation = FindSkipNavigation(name);
+ return navigation?.DeclaringEntityType == this ? navigation : null;
+ }
+
+ ///
+ ///
+ /// Gets all skip navigation properties declared on this entity type.
+ ///
+ ///
+ /// This method does not return skip navigation properties declared declared on base types.
+ /// It is useful when iterating over all entity types to avoid processing the same foreign key more than once.
+ /// Use to also return skip navigation properties declared on base types.
+ ///
+ ///
+ /// Declared foreign keys.
+ IEnumerable GetDeclaredSkipNavigations()
+ => GetSkipNavigations().Where(n => n.DeclaringEntityType == this);
+
+ ///
+ /// Gets all skip navigation properties on this entity type.
+ ///
+ /// All skip navigation properties on this entity type.
+ IEnumerable GetSkipNavigations();
+
///
/// Gets the index defined on the given properties. Returns null if no index is defined.
///
diff --git a/src/EFCore/Metadata/IForeignKey.cs b/src/EFCore/Metadata/IForeignKey.cs
index 794c0aa5bc0..6ecac7cf913 100644
--- a/src/EFCore/Metadata/IForeignKey.cs
+++ b/src/EFCore/Metadata/IForeignKey.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
+using System.Linq;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace Microsoft.EntityFrameworkCore.Metadata
@@ -68,5 +69,13 @@ public interface IForeignKey : IAnnotatable
/// principal is deleted or the relationship is severed.
///
DeleteBehavior DeleteBehavior { get; }
+
+ ///
+ /// Gets all skip navigations using this foreign key.
+ ///
+ /// The skip navigations using this foreign key.
+ IEnumerable GetReferencingSkipNavigations()
+ => PrincipalEntityType.GetSkipNavigations().Where(n => n.IsOnPrincipal && n.ForeignKey == this)
+ .Concat(DeclaringEntityType.GetSkipNavigations().Where(n => !n.IsOnPrincipal && n.ForeignKey == this));
}
}
diff --git a/src/EFCore/Metadata/IMutableEntityType.cs b/src/EFCore/Metadata/IMutableEntityType.cs
index 93e667589bd..23ce720e8a0 100644
--- a/src/EFCore/Metadata/IMutableEntityType.cs
+++ b/src/EFCore/Metadata/IMutableEntityType.cs
@@ -3,8 +3,10 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Utilities;
namespace Microsoft.EntityFrameworkCore.Metadata
{
@@ -125,6 +127,83 @@ IMutableForeignKey AddForeignKey(
/// The foreign key to be removed.
void RemoveForeignKey([NotNull] IMutableForeignKey foreignKey);
+ ///
+ /// Adds a new skip navigation properties to this entity type.
+ ///
+ /// The name of the skip navigation property to add.
+ ///
+ ///
+ /// The corresponding CLR type member or null for a shadow property.
+ ///
+ ///
+ /// An indexer with a string parameter and object return type can be used.
+ ///
+ ///
+ /// The entity type that the skip navigation property will hold an instance(s) of.
+ /// The foreign key to the association type.
+ /// Whether the navigation property is a collection property.
+ ///
+ /// Whether the navigation property is defined on the principal side of the underlying foreign key.
+ ///
+ /// The newly created skip navigation property.
+ IMutableSkipNavigation AddSkipNavigation(
+ [NotNull] string name,
+ [CanBeNull] MemberInfo memberInfo,
+ [NotNull] IMutableEntityType targetEntityType,
+ [NotNull] IMutableForeignKey foreignKey,
+ bool collection,
+ bool onPrincipal);
+
+ ///
+ /// Gets a skip navigation property on this entity type. Returns null if no navigation property is found.
+ ///
+ /// The navigation property on the entity class.
+ /// The navigation property, or null if none is found.
+ new IMutableSkipNavigation FindSkipNavigation([NotNull] MemberInfo memberInfo)
+ => (IMutableSkipNavigation)((IEntityType)this).FindSkipNavigation(memberInfo);
+
+ ///
+ /// Gets a skip navigation property on this entity type. Returns null if no skip navigation property is found.
+ ///
+ /// The name of the navigation property on the entity class.
+ /// The navigation property, or null if none is found.
+ new IMutableSkipNavigation FindSkipNavigation([NotNull] string name);
+
+ ///
+ /// Gets a skip navigation property on this entity type. Does not return skip navigation properties defined on a base type.
+ /// Returns null if no skip navigation property is found.
+ ///
+ /// The name of the navigation property on the entity class.
+ /// The navigation property, or null if none is found.
+ new IMutableSkipNavigation FindDeclaredSkipNavigation([NotNull] string name)
+ => (IMutableSkipNavigation)((IEntityType)this).FindDeclaredSkipNavigation(name);
+
+ ///
+ ///
+ /// Gets all skip navigation properties declared on this entity type.
+ ///
+ ///
+ /// This method does not return skip navigation properties declared declared on base types.
+ /// It is useful when iterating over all entity types to avoid processing the same foreign key more than once.
+ /// Use to also return skip navigation properties declared on base types.
+ ///
+ ///
+ /// Declared foreign keys.
+ new IEnumerable GetDeclaredSkipNavigations()
+ => ((IEntityType)this).GetDeclaredSkipNavigations().Cast();
+
+ ///
+ /// Gets all skip navigation properties on this entity type.
+ ///
+ /// All skip navigation properties on this entity type.
+ new IEnumerable GetSkipNavigations();
+
+ ///
+ /// Removes a skip navigation properties from this entity type.
+ ///
+ /// The skip navigation to be removed.
+ void RemoveSkipNavigation([NotNull] IMutableSkipNavigation navigation);
+
///
/// Adds an index to this entity type.
///
diff --git a/src/EFCore/Metadata/IMutableForeignKey.cs b/src/EFCore/Metadata/IMutableForeignKey.cs
index 26c1ce3f097..64284f7338c 100644
--- a/src/EFCore/Metadata/IMutableForeignKey.cs
+++ b/src/EFCore/Metadata/IMutableForeignKey.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
@@ -122,5 +123,12 @@ public interface IMutableForeignKey : IForeignKey, IMutableAnnotatable
///
/// The newly created navigation property.
IMutableNavigation HasPrincipalToDependent([CanBeNull] MemberInfo property);
+
+ ///
+ /// Gets all skip navigations using this foreign key.
+ ///
+ /// The skip navigations using this foreign key.
+ new IEnumerable GetReferencingSkipNavigations()
+ => ((IForeignKey)this).GetReferencingSkipNavigations().Cast();
}
}
diff --git a/src/EFCore/Metadata/IMutableNavigation.cs b/src/EFCore/Metadata/IMutableNavigation.cs
index ef96b910dae..cbd610e626b 100644
--- a/src/EFCore/Metadata/IMutableNavigation.cs
+++ b/src/EFCore/Metadata/IMutableNavigation.cs
@@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata
public interface IMutableNavigation : INavigation, IMutablePropertyBase
{
///
- /// Gets the type that this property belongs to.
+ /// Gets the type that this navigation property belongs to.
///
new IMutableEntityType DeclaringEntityType { get; }
diff --git a/src/EFCore/Metadata/IMutableSkipNavigation.cs b/src/EFCore/Metadata/IMutableSkipNavigation.cs
new file mode 100644
index 00000000000..c999e40e359
--- /dev/null
+++ b/src/EFCore/Metadata/IMutableSkipNavigation.cs
@@ -0,0 +1,53 @@
+// 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;
+
+namespace Microsoft.EntityFrameworkCore.Metadata
+{
+ ///
+ ///
+ /// Represents a navigation property that is part of a relationship
+ /// that is forwarded through a third entity type.
+ ///
+ ///
+ /// This interface is used during model creation and allows the metadata to be modified.
+ /// Once the model is built, represents a read-only view of the same metadata.
+ ///
+ ///
+ public interface IMutableSkipNavigation : ISkipNavigation, IMutablePropertyBase
+ {
+ ///
+ /// Gets the type that this navigation property belongs to.
+ ///
+ new IMutableEntityType DeclaringEntityType => IsOnPrincipal ? ForeignKey.PrincipalEntityType : ForeignKey.DeclaringEntityType;
+
+ ///
+ /// Gets the association type used by the foreign key.
+ ///
+ new IMutableEntityType AssociationEntityType => IsOnPrincipal ? ForeignKey.DeclaringEntityType : ForeignKey.PrincipalEntityType;
+
+ ///
+ /// Gets the entity type that this navigation property will hold an instance(s) of.
+ ///
+ new IMutableEntityType TargetEntityType { get; }
+
+ ///
+ /// Gets the foreign key to the association type.
+ ///
+ new IMutableForeignKey ForeignKey { get; }
+
+ ///
+ /// Gets the inverse skip navigation.
+ ///
+ new IMutableSkipNavigation Inverse { get; }
+
+ ///
+ /// Sets the inverse skip navigation.
+ ///
+ ///
+ /// The inverse skip navigation. Passing null will result in there being no inverse navigation property defined.
+ ///
+ IConventionSkipNavigation SetInverse([CanBeNull] IMutableSkipNavigation inverse);
+ }
+}
diff --git a/src/EFCore/Metadata/INavigation.cs b/src/EFCore/Metadata/INavigation.cs
index 239af697a8b..685e9f356d0 100644
--- a/src/EFCore/Metadata/INavigation.cs
+++ b/src/EFCore/Metadata/INavigation.cs
@@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata
public interface INavigation : IPropertyBase
{
///
- /// Gets the entity type that this property belongs to.
+ /// Gets the entity type that this navigation property belongs to.
///
IEntityType DeclaringEntityType { get; }
diff --git a/src/EFCore/Metadata/IPropertyBase.cs b/src/EFCore/Metadata/IPropertyBase.cs
index 9d2208fed88..1050c10374e 100644
--- a/src/EFCore/Metadata/IPropertyBase.cs
+++ b/src/EFCore/Metadata/IPropertyBase.cs
@@ -13,29 +13,29 @@ namespace Microsoft.EntityFrameworkCore.Metadata
public interface IPropertyBase : IAnnotatable
{
///
- /// Gets the name of the property.
+ /// Gets the name of this property-like object.
///
string Name { get; }
///
- /// Gets the type that this property belongs to.
+ /// Gets the type that this property-like object belongs to.
///
ITypeBase DeclaringType { get; }
///
- /// Gets the type of value that this property holds.
+ /// Gets the type of value that this property-like object holds.
///
Type ClrType { get; }
///
- /// Gets the for the underlying CLR property that this
- /// object represents. This may be null for shadow properties or properties mapped directly to fields.
+ /// Gets the for the underlying CLR property for this property-like object.
+ /// This may be null for shadow properties or if mapped directly to a field.
///
PropertyInfo PropertyInfo { get; }
///
- /// Gets the for the underlying CLR field for this property.
- /// This may be null for shadow properties or if the backing field for the property is not known.
+ /// Gets the for the underlying CLR field for this property-like object.
+ /// This may be null for shadow properties or if the backing field is not known.
///
FieldInfo FieldInfo { get; }
}
diff --git a/src/EFCore/Metadata/ISkipNavigation.cs b/src/EFCore/Metadata/ISkipNavigation.cs
new file mode 100644
index 00000000000..2ab04b220f0
--- /dev/null
+++ b/src/EFCore/Metadata/ISkipNavigation.cs
@@ -0,0 +1,63 @@
+// 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 Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+namespace Microsoft.EntityFrameworkCore.Metadata
+{
+ ///
+ /// Represents a navigation property that is part of a relationship
+ /// that is forwarded through a third entity type.
+ ///
+ public interface ISkipNavigation : IPropertyBase
+ {
+ ///
+ /// Gets the entity type that this navigation belongs to.
+ ///
+ IEntityType DeclaringEntityType => IsOnPrincipal ? ForeignKey.PrincipalEntityType : ForeignKey.DeclaringEntityType;
+
+ ///
+ /// Gets the association type used by the foreign key.
+ ///
+ IEntityType AssociationEntityType => IsOnPrincipal ? ForeignKey.DeclaringEntityType : ForeignKey.PrincipalEntityType;
+
+ ///
+ /// Gets the entity type that this navigation property will hold an instance(s) of.
+ ///
+ IEntityType TargetEntityType { get; }
+
+ ///
+ /// Gets the foreign key to the association type.
+ ///
+ IForeignKey ForeignKey { get; }
+
+ ///
+ /// Gets the inverse skip navigation.
+ ///
+ ISkipNavigation Inverse { get; }
+
+ ///
+ /// Gets a value indicating whether the navigation property is a collection property.
+ ///
+ bool IsCollection { get; }
+
+ ///
+ /// Gets a value indicating whether the navigation property is defined on the principal side of the underlying foreign key.
+ ///
+ bool IsOnPrincipal { get; }
+
+ ///
+ /// Gets a value indicating whether this navigation should be eager loaded by default.
+ ///
+ bool IsEagerLoaded
+ => (bool?)this[CoreAnnotationNames.EagerLoaded] ?? false;
+
+ ///
+ /// Gets the for this navigation property, which must be a collection
+ /// navigation.
+ ///
+ /// The accessor.
+ IClrCollectionAccessor GetCollectionAccessor()
+ => new ClrCollectionAccessorFactory().Create(this);
+ }
+}
diff --git a/src/EFCore/Metadata/Internal/ClrCollectionAccessorFactory.cs b/src/EFCore/Metadata/Internal/ClrCollectionAccessorFactory.cs
index 549f1306129..ea8801d2db9 100644
--- a/src/EFCore/Metadata/Internal/ClrCollectionAccessorFactory.cs
+++ b/src/EFCore/Metadata/Internal/ClrCollectionAccessorFactory.cs
@@ -49,27 +49,30 @@ private static readonly MethodInfo _createObservableHashSet
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual IClrCollectionAccessor Create([NotNull] INavigation navigation)
- {
- MemberInfo GetMostDerivedMemberInfo()
- {
- var propertyInfo = navigation.PropertyInfo;
- var fieldInfo = navigation.FieldInfo;
+ => !navigation.IsCollection() || navigation.IsShadowProperty() ? null : Create(navigation, navigation.GetTargetType());
- return fieldInfo == null
- ? propertyInfo
- : propertyInfo == null
- ? fieldInfo
- : fieldInfo.FieldType.IsAssignableFrom(propertyInfo.PropertyType)
- ? (MemberInfo)propertyInfo
- : fieldInfo;
- }
+ ///
+ /// 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.
+ ///
+ public virtual IClrCollectionAccessor Create([NotNull] ISkipNavigation navigation)
+ => !navigation.IsCollection || navigation.IsShadowProperty() ? null : Create(navigation, navigation.TargetEntityType);
+ private IClrCollectionAccessor Create(IPropertyBase navigation, IEntityType targetType)
+ {
// ReSharper disable once SuspiciousTypeConversion.Global
if (navigation is IClrCollectionAccessor accessor)
{
return accessor;
}
+ if (targetType == null)
+ {
+ return null;
+ }
+
var memberInfo = GetMostDerivedMemberInfo();
var propertyType = memberInfo.GetMemberType();
var elementType = propertyType.TryGetElementType(typeof(IEnumerable<>));
@@ -79,9 +82,9 @@ MemberInfo GetMostDerivedMemberInfo()
throw new InvalidOperationException(
CoreStrings.NavigationBadType(
navigation.Name,
- navigation.DeclaringEntityType.DisplayName(),
+ navigation.DeclaringType.DisplayName(),
propertyType.ShortDisplayName(),
- navigation.GetTargetType().DisplayName()));
+ targetType.DisplayName()));
}
if (propertyType.IsArray)
@@ -89,7 +92,7 @@ MemberInfo GetMostDerivedMemberInfo()
throw new InvalidOperationException(
CoreStrings.NavigationArray(
navigation.Name,
- navigation.DeclaringEntityType.DisplayName(),
+ navigation.DeclaringType.DisplayName(),
propertyType.ShortDisplayName()));
}
@@ -105,6 +108,20 @@ MemberInfo GetMostDerivedMemberInfo()
{
throw invocationException.InnerException;
}
+
+ MemberInfo GetMostDerivedMemberInfo()
+ {
+ var propertyInfo = navigation.PropertyInfo;
+ var fieldInfo = navigation.FieldInfo;
+
+ return fieldInfo == null
+ ? propertyInfo
+ : propertyInfo == null
+ ? fieldInfo
+ : fieldInfo.FieldType.IsAssignableFrom(propertyInfo.PropertyType)
+ ? (MemberInfo)propertyInfo
+ : fieldInfo;
+ }
}
[UsedImplicitly]
diff --git a/src/EFCore/Metadata/Internal/EntityType.cs b/src/EFCore/Metadata/Internal/EntityType.cs
index 3f4c6a341f6..81f540f5689 100644
--- a/src/EFCore/Metadata/Internal/EntityType.cs
+++ b/src/EFCore/Metadata/Internal/EntityType.cs
@@ -34,6 +34,9 @@ private readonly SortedSet _foreignKeys
private readonly SortedDictionary _navigations
= new SortedDictionary(StringComparer.Ordinal);
+ private readonly SortedDictionary _skipNavigations
+ = new SortedDictionary(StringComparer.Ordinal);
+
private readonly SortedDictionary, Index> _indexes
= new SortedDictionary, Index>(PropertyListComparer.Instance);
@@ -73,7 +76,7 @@ private readonly SortedDictionary _serviceProperties
public EntityType([NotNull] string name, [NotNull] Model model, ConfigurationSource configurationSource)
: base(name, model, configurationSource)
{
- _properties = new SortedDictionary(new PropertyComparer(this));
+ _properties = new SortedDictionary(new PropertyNameComparer(this));
Builder = new InternalEntityTypeBuilder(this, model.Builder);
}
@@ -91,7 +94,7 @@ public EntityType([NotNull] Type clrType, [NotNull] Model model, ConfigurationSo
throw new ArgumentException(CoreStrings.InvalidEntityType(clrType));
}
- _properties = new SortedDictionary(new PropertyComparer(this));
+ _properties = new SortedDictionary(new PropertyNameComparer(this));
Builder = new InternalEntityTypeBuilder(this, model.Builder);
}
@@ -139,8 +142,8 @@ public EntityType(
///
public virtual InternalEntityTypeBuilder Builder
{
- [DebuggerStepThrough] get;
- [DebuggerStepThrough]
+ get;
+
[param: CanBeNull]
set;
}
@@ -187,9 +190,7 @@ public virtual bool IsKeyless
/// 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.
///
- public virtual void HasNoKey(
- bool? keyless,
- ConfigurationSource configurationSource)
+ public virtual void HasNoKey(bool? keyless, ConfigurationSource configurationSource)
{
if (_isKeyless == keyless)
{
@@ -246,9 +247,7 @@ public virtual void UpdateIsKeylessConfigurationSource(ConfigurationSource confi
/// 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.
///
- public virtual void HasBaseType(
- [CanBeNull] EntityType newBaseType,
- ConfigurationSource configurationSource)
+ public virtual void HasBaseType([CanBeNull] EntityType newBaseType, ConfigurationSource configurationSource)
{
Check.DebugAssert(Builder != null, "Builder is null");
@@ -485,7 +484,8 @@ protected override IConventionAnnotation OnAnnotationSet(
public virtual IEnumerable FindMembersInHierarchy([NotNull] string name)
=> FindPropertiesInHierarchy(name).Cast()
.Concat(FindServicePropertiesInHierarchy(name))
- .Concat(FindNavigationsInHierarchy(name));
+ .Concat(FindNavigationsInHierarchy(name))
+ .Concat(FindSkipNavigationsInHierarchy(name));
#region Primary and Candidate Keys
@@ -804,12 +804,13 @@ public virtual Key RemoveKey([NotNull] IReadOnlyList properties)
///
public virtual Key RemoveKey([NotNull] Key key)
{
+ Check.NotNull(key, nameof(key));
Check.DebugAssert(Builder != null, "Builder is null");
if (key.DeclaringEntityType != this)
{
throw new InvalidOperationException(
- CoreStrings.KeyWrongType(key.Properties.Format(), key.DeclaringEntityType.DisplayName(), this.DisplayName()));
+ CoreStrings.KeyWrongType(key.Properties.Format(), this.DisplayName(), key.DeclaringEntityType.DisplayName()));
}
CheckKeyNotInUse(key);
@@ -844,8 +845,11 @@ private void CheckKeyNotInUse(Key key)
var foreignKey = key.GetReferencingForeignKeys().FirstOrDefault();
if (foreignKey != null)
{
- throw new InvalidOperationException(
- CoreStrings.KeyInUse(key.Properties.Format(), this.DisplayName(), foreignKey.DeclaringEntityType.DisplayName()));
+ throw new InvalidOperationException(CoreStrings.KeyInUse(
+ key.Properties.Format(),
+ this.DisplayName(),
+ foreignKey.Properties.Format(),
+ foreignKey.DeclaringEntityType.DisplayName()));
}
}
@@ -1200,6 +1204,9 @@ public virtual ForeignKey RemoveForeignKey(
///
public virtual ForeignKey RemoveForeignKey([NotNull] ForeignKey foreignKey)
{
+ Check.NotNull(foreignKey, nameof(foreignKey));
+ Check.DebugAssert(Builder != null, "Builder is null");
+
if (foreignKey.DeclaringEntityType != this)
{
throw new InvalidOperationException(
@@ -1207,8 +1214,19 @@ public virtual ForeignKey RemoveForeignKey([NotNull] ForeignKey foreignKey)
foreignKey.Properties.Format(),
foreignKey.PrincipalKey.Properties.Format(),
foreignKey.PrincipalEntityType.DisplayName(),
- foreignKey.DeclaringEntityType.DisplayName(),
- this.DisplayName()));
+ this.DisplayName(),
+ foreignKey.DeclaringEntityType.DisplayName()));
+ }
+
+ var referencingSkipNavigation = foreignKey.ReferencingSkipNavigations?.FirstOrDefault();
+ if (referencingSkipNavigation != null)
+ {
+ throw new InvalidOperationException(
+ CoreStrings.ForeignKeyInUseSkipNavigation(
+ foreignKey.Properties.Format(),
+ this.DisplayName(),
+ referencingSkipNavigation.Name,
+ referencingSkipNavigation.DeclaringType.DisplayName()));
}
if (foreignKey.DependentToPrincipal != null)
@@ -1243,9 +1261,7 @@ public virtual ForeignKey RemoveForeignKey([NotNull] ForeignKey foreignKey)
foreignKey.PrincipalToDependent.GetIdentifyingMemberInfo());
}
- Model.ConventionDispatcher.OnForeignKeyRemoved(Builder, foreignKey);
-
- return foreignKey;
+ return (ForeignKey)Model.ConventionDispatcher.OnForeignKeyRemoved(Builder, foreignKey);
}
///
@@ -1306,19 +1322,19 @@ public virtual Navigation AddNavigation(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual Navigation AddNavigation(
- [NotNull] MemberInfo navigationProperty,
+ [NotNull] MemberInfo navigationMember,
[NotNull] ForeignKey foreignKey,
bool pointsToPrincipal)
{
- Check.NotNull(navigationProperty, nameof(navigationProperty));
+ Check.NotNull(navigationMember, nameof(navigationMember));
Check.NotNull(foreignKey, nameof(foreignKey));
- return AddNavigation(new MemberIdentity(navigationProperty), foreignKey, pointsToPrincipal);
+ return AddNavigation(new MemberIdentity(navigationMember), foreignKey, pointsToPrincipal);
}
- private Navigation AddNavigation(MemberIdentity propertyIdentity, ForeignKey foreignKey, bool pointsToPrincipal)
+ private Navigation AddNavigation(MemberIdentity navigationMember, ForeignKey foreignKey, bool pointsToPrincipal)
{
- var name = propertyIdentity.Name;
+ var name = navigationMember.Name;
var duplicateNavigation = FindNavigationsInHierarchy(name).FirstOrDefault();
if (duplicateNavigation != null)
{
@@ -1337,14 +1353,12 @@ private Navigation AddNavigation(MemberIdentity propertyIdentity, ForeignKey for
name, this.DisplayName(), duplicateNavigation.DeclaringEntityType.DisplayName()));
}
- var duplicateProperty = FindPropertiesInHierarchy(name).Cast()
- .Concat(FindServicePropertiesInHierarchy(name)).FirstOrDefault();
+ var duplicateProperty = FindMembersInHierarchy(name).FirstOrDefault();
if (duplicateProperty != null)
{
throw new InvalidOperationException(
CoreStrings.ConflictingPropertyOrNavigation(
- name, this.DisplayName(),
- duplicateProperty.DeclaringType.DisplayName()));
+ name, this.DisplayName(), duplicateProperty.DeclaringType.DisplayName()));
}
Check.DebugAssert(
@@ -1355,20 +1369,28 @@ private Navigation AddNavigation(MemberIdentity propertyIdentity, ForeignKey for
(pointsToPrincipal ? foreignKey.DeclaringEntityType : foreignKey.PrincipalEntityType) == this,
"EntityType mismatch");
- var navigationProperty = propertyIdentity.MemberInfo
- ?? ClrType?.GetMembersInHierarchy(name).FirstOrDefault();
+ var memberInfo = navigationMember.MemberInfo;
+ if (memberInfo != null)
+ {
+ ValidateClrMember(name, memberInfo);
+ }
+ else
+ {
+ memberInfo = ClrType?.GetMembersInHierarchy(name).FirstOrDefault();
+ }
+
if (ClrType != null)
{
Navigation.IsCompatible(
- propertyIdentity.Name,
- navigationProperty,
+ name,
+ memberInfo,
this,
pointsToPrincipal ? foreignKey.PrincipalEntityType : foreignKey.DeclaringEntityType,
!pointsToPrincipal && !foreignKey.IsUnique,
shouldThrow: true);
}
- var navigation = new Navigation(name, navigationProperty as PropertyInfo, navigationProperty as FieldInfo, foreignKey);
+ var navigation = new Navigation(name, memberInfo as PropertyInfo, memberInfo as FieldInfo, foreignKey);
_navigations.Add(name, navigation);
@@ -1440,11 +1462,11 @@ public virtual IEnumerable GetDerivedNavigationsInclusive()
/// 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.
///
- public virtual IEnumerable FindDerivedNavigations([NotNull] string navigationName)
+ public virtual IEnumerable FindDerivedNavigations([NotNull] string name)
{
- Check.NotNull(navigationName, nameof(navigationName));
+ Check.NotNull(name, nameof(name));
- return GetDerivedTypes().Select(et => et.FindDeclaredNavigation(navigationName)).Where(n => n != null);
+ return GetDerivedTypes().Select(et => et.FindDeclaredNavigation(name)).Where(n => n != null);
}
///
@@ -1453,8 +1475,8 @@ public virtual IEnumerable FindDerivedNavigations([NotNull] string n
/// 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.
///
- public virtual IEnumerable FindNavigationsInHierarchy([NotNull] string navigationName)
- => ToEnumerable(FindNavigation(navigationName)).Concat(FindDerivedNavigations(navigationName));
+ public virtual IEnumerable FindNavigationsInHierarchy([NotNull] string name)
+ => ToEnumerable(FindNavigation(name)).Concat(FindDerivedNavigations(name));
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -1486,6 +1508,257 @@ public virtual Navigation RemoveNavigation([NotNull] string name)
public virtual IEnumerable GetNavigations()
=> _baseType?.GetNavigations().Concat(_navigations.Values) ?? _navigations.Values;
+ ///
+ /// 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.
+ ///
+ public virtual SkipNavigation AddSkipNavigation(
+ [NotNull] string name,
+ [CanBeNull] MemberInfo memberInfo,
+ [NotNull] EntityType targetEntityType,
+ [NotNull] ForeignKey foreignKey,
+ bool collection,
+ bool onPrincipal,
+ ConfigurationSource configurationSource)
+ {
+ Check.NotEmpty(name, nameof(name));
+ Check.NotNull(targetEntityType, nameof(targetEntityType));
+ Check.NotNull(foreignKey, nameof(foreignKey));
+
+ var duplicateProperty = FindMembersInHierarchy(name).FirstOrDefault();
+ if (duplicateProperty != null)
+ {
+ throw new InvalidOperationException(
+ CoreStrings.ConflictingPropertyOrNavigation(
+ name, this.DisplayName(), duplicateProperty.DeclaringType.DisplayName()));
+ }
+
+ if (memberInfo != null)
+ {
+ ValidateClrMember(name, memberInfo);
+ }
+ else
+ {
+ memberInfo = ClrType?.GetMembersInHierarchy(name).FirstOrDefault();
+ }
+
+ if (ClrType != null)
+ {
+ Navigation.IsCompatible(
+ name,
+ memberInfo,
+ this,
+ targetEntityType,
+ collection,
+ shouldThrow: true);
+ }
+
+ var expectedEntityType = onPrincipal ? foreignKey.PrincipalEntityType : foreignKey.DeclaringEntityType;
+ if (expectedEntityType != this)
+ {
+ var message = onPrincipal
+ ? CoreStrings.SkipNavigationWrongPrincipalType(
+ name, this.DisplayName(), expectedEntityType.DisplayName(), foreignKey.Properties.Format())
+ : CoreStrings.SkipNavigationWrongDependentType(
+ name, this.DisplayName(), expectedEntityType.DisplayName(), foreignKey.Properties.Format());
+ throw new InvalidOperationException(message);
+ }
+
+ var skipNavigation = new SkipNavigation(
+ name,
+ memberInfo as PropertyInfo,
+ memberInfo as FieldInfo,
+ targetEntityType,
+ foreignKey,
+ collection,
+ onPrincipal,
+ configurationSource);
+
+ _skipNavigations.Add(name, skipNavigation);
+
+ if (foreignKey.ReferencingSkipNavigations == null)
+ {
+ foreignKey.ReferencingSkipNavigations = new SortedSet(SkipNavigationComparer.Instance) { skipNavigation };
+ }
+ else
+ {
+ foreignKey.ReferencingSkipNavigations.Add(skipNavigation);
+ }
+
+ return (SkipNavigation)Model.ConventionDispatcher.OnSkipNavigationAdded(skipNavigation.Builder)?.Metadata;
+ }
+
+ private Type ValidateClrMember(string name, MemberInfo memberInfo, bool throwOnNameMismatch = true)
+ {
+ if (ClrType == null)
+ {
+ throw new InvalidOperationException(CoreStrings.ClrPropertyOnShadowEntity(memberInfo.Name, this.DisplayName()));
+ }
+
+ if (name != memberInfo.GetSimpleMemberName())
+ {
+ if ((memberInfo as PropertyInfo)?.IsEFIndexerProperty() != true)
+ {
+ if (throwOnNameMismatch)
+ {
+ throw new InvalidOperationException(
+ CoreStrings.PropertyWrongName(
+ name,
+ this.DisplayName(),
+ memberInfo.GetSimpleMemberName()));
+ }
+
+ return memberInfo.GetMemberType();
+ }
+ else
+ {
+ var clashingMemberInfo = ClrType?.GetMembersInHierarchy(name).FirstOrDefault();
+ if (clashingMemberInfo != null)
+ {
+ throw new InvalidOperationException(
+ CoreStrings.PropertyClashingNonIndexer(
+ name,
+ this.DisplayName()));
+ }
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// 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.
+ ///
+ public virtual SkipNavigation FindSkipNavigation([NotNull] string name)
+ {
+ Check.NotEmpty(name, nameof(name));
+
+ return FindDeclaredSkipNavigation(name) ?? _baseType?.FindSkipNavigation(name);
+ }
+
+ ///
+ /// 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.
+ ///
+ public virtual SkipNavigation FindSkipNavigation([NotNull] MemberInfo memberInfo)
+ => FindSkipNavigation(Check.NotNull(memberInfo, nameof(memberInfo)).GetSimpleMemberName());
+
+ ///
+ /// 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.
+ ///
+ public virtual SkipNavigation FindDeclaredSkipNavigation([NotNull] string name)
+ => _skipNavigations.TryGetValue(Check.NotEmpty(name, nameof(name)), out var navigation)
+ ? navigation
+ : null;
+
+ ///
+ /// 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.
+ ///
+ public virtual IEnumerable GetDeclaredSkipNavigations() => _skipNavigations.Values;
+
+ ///
+ /// 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.
+ ///
+ public virtual IEnumerable GetDerivedSkipNavigations()
+ => GetDerivedTypes().SelectMany(et => et.GetDeclaredSkipNavigations());
+
+ ///
+ /// 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.
+ ///
+ public virtual IEnumerable GetDerivedSkipNavigationsInclusive()
+ => GetDerivedTypesInclusive().SelectMany(et => et.GetDeclaredSkipNavigations());
+
+ ///
+ /// 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.
+ ///
+ public virtual IEnumerable FindDerivedSkipNavigations([NotNull] string name)
+ {
+ Check.NotNull(name, nameof(name));
+
+ return GetDerivedTypes().Select(et => et.FindDeclaredSkipNavigation(name)).Where(n => n != null);
+ }
+
+ ///
+ /// 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.
+ ///
+ public virtual IEnumerable FindSkipNavigationsInHierarchy([NotNull] string name)
+ => ToEnumerable(FindSkipNavigation(name)).Concat(FindDerivedSkipNavigations(name));
+
+ ///
+ /// 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.
+ ///
+ public virtual SkipNavigation RemoveSkipNavigation([NotNull] string name)
+ {
+ Check.NotEmpty(name, nameof(name));
+
+ var navigation = FindDeclaredSkipNavigation(name);
+ return navigation == null ? null : RemoveSkipNavigation(navigation);
+ }
+
+ ///
+ /// 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.
+ ///
+ public virtual SkipNavigation RemoveSkipNavigation([NotNull] SkipNavigation navigation)
+ {
+ Check.NotNull(navigation, nameof(navigation));
+ Check.DebugAssert(Builder != null, "Builder is null");
+
+ if (navigation.DeclaringType != this)
+ {
+ throw new InvalidOperationException(CoreStrings.SkipNavigationWrongType(
+ navigation.Name, this.DisplayName(), navigation.DeclaringType.DisplayName()));
+ }
+
+ var removed = _skipNavigations.Remove(navigation.Name);
+ Check.DebugAssert(removed, "Expected the navigation to be removed");
+
+ navigation.ForeignKey.ReferencingSkipNavigations.Remove(navigation);
+
+ navigation.Builder = null;
+
+ return (SkipNavigation)Model.ConventionDispatcher.OnSkipNavigationRemoved(Builder, navigation);
+ }
+
+ ///
+ /// 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.
+ ///
+ public virtual IEnumerable GetSkipNavigations()
+ => _baseType?.GetSkipNavigations().Concat(_skipNavigations.Values) ?? _skipNavigations.Values;
+
#endregion
#region Indexes
@@ -1652,10 +1925,13 @@ public virtual Index RemoveIndex([NotNull] IReadOnlyList properties)
///
public virtual Index RemoveIndex([NotNull] Index index)
{
+ Check.NotNull(index, nameof(index));
+ Check.DebugAssert(Builder != null, "Builder is null");
+
if (!_indexes.Remove(index.Properties))
{
throw new InvalidOperationException(
- CoreStrings.IndexWrongType(index.Properties.Format(), index.DeclaringEntityType.DisplayName(), this.DisplayName()));
+ CoreStrings.IndexWrongType(index.Properties.Format(), this.DisplayName(), index.DeclaringEntityType.DisplayName()));
}
index.Builder = null;
@@ -1672,9 +1948,7 @@ public virtual Index RemoveIndex([NotNull] Index index)
}
}
- Model.ConventionDispatcher.OnIndexRemoved(Builder, index);
-
- return index;
+ return (Index)Model.ConventionDispatcher.OnIndexRemoved(Builder, index);
}
///
@@ -1775,45 +2049,15 @@ public virtual Property AddProperty(
if (memberInfo != null)
{
- if (ClrType == null)
- {
- throw new InvalidOperationException(CoreStrings.ClrPropertyOnShadowEntity(memberInfo.Name, this.DisplayName()));
- }
+ propertyType = ValidateClrMember(name, memberInfo, typeConfigurationSource != null)
+ ?? propertyType;
if (memberInfo.DeclaringType?.IsAssignableFrom(ClrType) != true)
{
- throw new ArgumentException(
+ throw new InvalidOperationException(
CoreStrings.PropertyWrongEntityClrType(
memberInfo.Name, this.DisplayName(), memberInfo.DeclaringType?.ShortDisplayName()));
}
-
- if (name != memberInfo.GetSimpleMemberName())
- {
- if ((memberInfo as PropertyInfo)?.IsEFIndexerProperty() != true)
- {
- if (typeConfigurationSource != null)
- {
- throw new InvalidOperationException(
- CoreStrings.PropertyWrongName(
- name,
- this.DisplayName(),
- memberInfo.GetSimpleMemberName()));
- }
-
- propertyType = memberInfo.GetMemberType();
- }
- else
- {
- var clashingMemberInfo = ClrType?.GetMembersInHierarchy(name).FirstOrDefault();
- if (clashingMemberInfo != null)
- {
- throw new InvalidOperationException(
- CoreStrings.PropertyClashingNonIndexer(
- name,
- this.DisplayName()));
- }
- }
- }
}
else
{
@@ -1952,22 +2196,26 @@ public virtual Property RemoveProperty([NotNull] string name)
///
public virtual Property RemoveProperty([NotNull] Property property)
{
+ Check.NotNull(property, nameof(property));
+ Check.DebugAssert(Builder != null, "Builder is null");
+
if (property.DeclaringEntityType != this)
{
throw new InvalidOperationException(
CoreStrings.PropertyWrongType(
property.Name,
- property.DeclaringEntityType.DisplayName(),
- this.DisplayName()));
+ this.DisplayName(),
+ property.DeclaringEntityType.DisplayName()));
}
CheckPropertyNotInUse(property);
var removed = _properties.Remove(property.Name);
Check.DebugAssert(removed, "removed is false");
+
property.Builder = null;
- return property;
+ return (Property)Model.ConventionDispatcher.OnPropertyRemoved(Builder, property);
}
private void CheckPropertyNotInUse(Property property)
@@ -2109,6 +2357,8 @@ public virtual ServiceProperty AddServiceProperty(
duplicateMember.DeclaringType.DisplayName()));
}
+ ValidateClrMember(name, memberInfo, false);
+
var serviceProperty = new ServiceProperty(
name,
memberInfo as PropertyInfo,
@@ -2209,9 +2459,29 @@ public virtual ServiceProperty RemoveServiceProperty([NotNull] string name)
: RemoveServiceProperty(property);
}
- private ServiceProperty RemoveServiceProperty(ServiceProperty property)
+ ///
+ /// 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.
+ ///
+ public virtual ServiceProperty RemoveServiceProperty([NotNull] ServiceProperty property)
{
- _serviceProperties.Remove(property.Name);
+ Check.NotNull(property, nameof(property));
+ Check.DebugAssert(Builder != null, "Builder is null");
+
+ if (property.DeclaringEntityType != this)
+ {
+ throw new InvalidOperationException(
+ CoreStrings.PropertyWrongType(
+ property.Name,
+ this.DisplayName(),
+ property.DeclaringEntityType.DisplayName()));
+ }
+
+ var removed = _serviceProperties.Remove(property.Name);
+ Check.DebugAssert(removed, "removed is false");
+
property.Builder = null;
return property;
@@ -2526,9 +2796,10 @@ public virtual void CheckDiscriminatorValue([NotNull] IEntityType entityType, [C
/// 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.
///
- IModel ITypeBase.Model
+ IConventionEntityTypeBuilder IConventionEntityType.Builder
{
- [DebuggerStepThrough] get => Model;
+ [DebuggerStepThrough]
+ get => Builder;
}
///
@@ -2537,7 +2808,7 @@ IModel ITypeBase.Model
/// 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.
///
- IMutableModel IMutableTypeBase.Model
+ IModel ITypeBase.Model
{
[DebuggerStepThrough] get => Model;
}
@@ -2548,7 +2819,7 @@ IMutableModel IMutableTypeBase.Model
/// 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.
///
- IMutableModel IMutableEntityType.Model
+ IMutableModel IMutableTypeBase.Model
{
[DebuggerStepThrough] get => Model;
}
@@ -2559,9 +2830,32 @@ IMutableModel IMutableEntityType.Model
/// 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.
///
- IEntityType IEntityType.BaseType
+ IMutableModel IMutableEntityType.Model
{
- [DebuggerStepThrough] get => _baseType;
+ [DebuggerStepThrough] get => Model;
+ }
+
+ ///
+ /// 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.
+ ///
+ IConventionModel IConventionEntityType.Model
+ {
+ [DebuggerStepThrough]
+ get => Model;
+ }
+
+ ///
+ /// 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.
+ ///
+ IEntityType IEntityType.BaseType
+ {
+ [DebuggerStepThrough] get => _baseType;
}
///
@@ -2576,6 +2870,18 @@ IMutableEntityType IMutableEntityType.BaseType
set => HasBaseType((EntityType)value, ConfigurationSource.Explicit);
}
+ ///
+ /// 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.
+ ///
+ IConventionEntityType IConventionEntityType.BaseType
+ {
+ [DebuggerStepThrough]
+ get => BaseType;
+ }
+
///
/// 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
@@ -2615,9 +2921,42 @@ IConventionEntityType IConventionEntityType.DefiningEntityType
/// 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.
///
+ void IConventionEntityType.HasBaseType(IConventionEntityType entityType, bool fromDataAnnotation)
+ => HasBaseType(
+ (EntityType)entityType, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// 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.
+ ///
+ [DebuggerStepThrough]
+ void IConventionEntityType.HasNoKey(bool? keyless, bool fromDataAnnotation)
+ => HasNoKey(keyless, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// 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.
+ ///
+ [DebuggerStepThrough]
IMutableKey IMutableEntityType.SetPrimaryKey(IReadOnlyList properties)
=> SetPrimaryKey(properties?.Cast().ToList(), ConfigurationSource.Explicit);
+ ///
+ /// 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.
+ ///
+ [DebuggerStepThrough]
+ IConventionKey IConventionEntityType.SetPrimaryKey(IReadOnlyList properties, bool fromDataAnnotation)
+ => SetPrimaryKey(
+ properties?.Cast().ToList(),
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
///
/// 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
@@ -2642,9 +2981,31 @@ IMutableKey IMutableEntityType.SetPrimaryKey(IReadOnlyList pro
/// 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.
///
+ [DebuggerStepThrough]
+ IConventionKey IConventionEntityType.FindPrimaryKey() => FindPrimaryKey();
+
+ ///
+ /// 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.
+ ///
+ [DebuggerStepThrough]
IMutableKey IMutableEntityType.AddKey(IReadOnlyList properties)
=> AddKey(properties.Cast().ToList(), ConfigurationSource.Explicit);
+ ///
+ /// 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.
+ ///
+ [DebuggerStepThrough]
+ IConventionKey IConventionEntityType.AddKey(IReadOnlyList properties, bool fromDataAnnotation)
+ => AddKey(
+ properties.Cast().ToList(),
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
///
/// 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
@@ -2669,6 +3030,16 @@ IMutableKey IMutableEntityType.AddKey(IReadOnlyList properties
/// 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.
///
+ [DebuggerStepThrough]
+ IConventionKey IConventionEntityType.FindKey(IReadOnlyList properties) => FindKey(properties);
+
+ ///
+ /// 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.
+ ///
+ [DebuggerStepThrough]
IEnumerable IEntityType.GetKeys() => GetKeys();
///
@@ -2677,6 +3048,7 @@ IMutableKey IMutableEntityType.AddKey(IReadOnlyList properties
/// 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.
///
+ [DebuggerStepThrough]
IEnumerable IMutableEntityType.GetKeys() => GetKeys();
///
@@ -2685,6 +3057,16 @@ IMutableKey IMutableEntityType.AddKey(IReadOnlyList properties
/// 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.
///
+ [DebuggerStepThrough]
+ IEnumerable IConventionEntityType.GetKeys() => GetKeys();
+
+ ///
+ /// 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.
+ ///
+ [DebuggerStepThrough]
void IMutableEntityType.RemoveKey(IMutableKey key) => RemoveKey((Key)key);
///
@@ -2693,6 +3075,16 @@ IMutableKey IMutableEntityType.AddKey(IReadOnlyList properties
/// 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.
///
+ [DebuggerStepThrough]
+ void IConventionEntityType.RemoveKey(IConventionKey key) => RemoveKey((Key)key);
+
+ ///
+ /// 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.
+ ///
+ [DebuggerStepThrough]
IMutableForeignKey IMutableEntityType.AddForeignKey(
IReadOnlyList properties, IMutableKey principalKey, IMutableEntityType principalEntityType)
=> AddForeignKey(
@@ -2702,6 +3094,39 @@ IMutableForeignKey IMutableEntityType.AddForeignKey(
ConfigurationSource.Explicit,
ConfigurationSource.Explicit);
+ ///
+ /// 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.
+ ///
+ [DebuggerStepThrough]
+ IConventionForeignKey IConventionEntityType.AddForeignKey(
+ IReadOnlyList properties,
+ IConventionKey principalKey,
+ IConventionEntityType principalEntityType,
+ bool setComponentConfigurationSource,
+ bool fromDataAnnotation)
+ => AddForeignKey(
+ properties.Cast().ToList(),
+ (Key)principalKey,
+ (EntityType)principalEntityType,
+ setComponentConfigurationSource
+ ? fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention
+ : (ConfigurationSource?)null,
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// 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.
+ ///
+ [DebuggerStepThrough]
+ IForeignKey IEntityType.FindForeignKey(
+ IReadOnlyList properties, IKey principalKey, IEntityType principalEntityType)
+ => FindForeignKey(properties, principalKey, principalEntityType);
+
///
/// 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
@@ -2720,7 +3145,8 @@ IMutableForeignKey IMutableEntityType.FindForeignKey(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- IForeignKey IEntityType.FindForeignKey(IReadOnlyList properties, IKey principalKey, IEntityType principalEntityType)
+ IConventionForeignKey IConventionEntityType.FindForeignKey(
+ IReadOnlyList properties, IKey principalKey, IEntityType principalEntityType)
=> FindForeignKey(properties, principalKey, principalEntityType);
///
@@ -2729,6 +3155,7 @@ IForeignKey IEntityType.FindForeignKey(IReadOnlyList properties, IKey
/// 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.
///
+ [DebuggerStepThrough]
IEnumerable IEntityType.GetForeignKeys() => GetForeignKeys();
///
@@ -2737,6 +3164,7 @@ IForeignKey IEntityType.FindForeignKey(IReadOnlyList properties, IKey
/// 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.
///
+ [DebuggerStepThrough]
IEnumerable IMutableEntityType.GetForeignKeys() => GetForeignKeys();
///
@@ -2745,8 +3173,8 @@ IForeignKey IEntityType.FindForeignKey(IReadOnlyList properties, IKey
/// 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.
///
- void IMutableEntityType.RemoveForeignKey(IMutableForeignKey foreignKey)
- => RemoveForeignKey((ForeignKey)foreignKey);
+ [DebuggerStepThrough]
+ IEnumerable IConventionEntityType.GetForeignKeys() => GetForeignKeys();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2754,8 +3182,9 @@ void IMutableEntityType.RemoveForeignKey(IMutableForeignKey foreignKey)
/// 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.
///
- IMutableIndex IMutableEntityType.AddIndex(IReadOnlyList properties)
- => AddIndex(properties.Cast().ToList(), ConfigurationSource.Explicit);
+ [DebuggerStepThrough]
+ void IMutableEntityType.RemoveForeignKey(IMutableForeignKey foreignKey)
+ => RemoveForeignKey((ForeignKey)foreignKey);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2764,7 +3193,8 @@ IMutableIndex IMutableEntityType.AddIndex(IReadOnlyList proper
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- IIndex IEntityType.FindIndex(IReadOnlyList properties) => FindIndex(properties);
+ void IConventionEntityType.RemoveForeignKey(IConventionForeignKey foreignKey)
+ => RemoveForeignKey((ForeignKey)foreignKey);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2773,7 +3203,15 @@ IMutableIndex IMutableEntityType.AddIndex(IReadOnlyList proper
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- IMutableIndex IMutableEntityType.FindIndex(IReadOnlyList properties) => FindIndex(properties);
+ IMutableSkipNavigation IMutableEntityType.AddSkipNavigation(
+ [NotNull] string name,
+ [CanBeNull] MemberInfo memberInfo,
+ [NotNull] IMutableEntityType targetEntityType,
+ [NotNull] IMutableForeignKey foreignKey,
+ bool collection,
+ bool onPrincipal)
+ => AddSkipNavigation(name, memberInfo, (EntityType)targetEntityType, (ForeignKey)foreignKey, collection, onPrincipal,
+ ConfigurationSource.Explicit);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2781,7 +3219,17 @@ IMutableIndex IMutableEntityType.AddIndex(IReadOnlyList proper
/// 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.
///
- IEnumerable IEntityType.GetIndexes() => GetIndexes();
+ [DebuggerStepThrough]
+ IConventionSkipNavigation IConventionEntityType.AddSkipNavigation(
+ [NotNull] string name,
+ [CanBeNull] MemberInfo memberInfo,
+ [NotNull] IConventionEntityType targetEntityType,
+ [NotNull] IConventionForeignKey foreignKey,
+ bool collection,
+ bool onPrincipal,
+ bool fromDataAnnotation)
+ => AddSkipNavigation(name, memberInfo, (EntityType)targetEntityType, (ForeignKey)foreignKey, collection, onPrincipal,
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2789,7 +3237,9 @@ IMutableIndex IMutableEntityType.AddIndex(IReadOnlyList proper
/// 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.
///
- IEnumerable IMutableEntityType.GetIndexes() => GetIndexes();
+ [DebuggerStepThrough]
+ ISkipNavigation IEntityType.FindSkipNavigation(MemberInfo memberInfo)
+ => FindSkipNavigation(memberInfo);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2797,7 +3247,9 @@ IMutableIndex IMutableEntityType.AddIndex(IReadOnlyList proper
/// 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.
///
- void IMutableEntityType.RemoveIndex(IMutableIndex index) => RemoveIndex((Index)index);
+ [DebuggerStepThrough]
+ ISkipNavigation IEntityType.FindSkipNavigation(string name)
+ => FindSkipNavigation(name);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2805,8 +3257,9 @@ IMutableIndex IMutableEntityType.AddIndex(IReadOnlyList proper
/// 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.
///
- IMutableProperty IMutableEntityType.AddProperty(string name, Type propertyType, MemberInfo memberInfo)
- => AddProperty(name, propertyType, memberInfo, ConfigurationSource.Explicit, ConfigurationSource.Explicit);
+ [DebuggerStepThrough]
+ IMutableSkipNavigation IMutableEntityType.FindSkipNavigation(string name)
+ => FindSkipNavigation(name);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2815,7 +3268,8 @@ IMutableProperty IMutableEntityType.AddProperty(string name, Type propertyType,
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- IProperty IEntityType.FindProperty(string name) => FindProperty(name);
+ IConventionSkipNavigation IConventionEntityType.FindSkipNavigation(string name)
+ => FindSkipNavigation(name);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2824,7 +3278,8 @@ IMutableProperty IMutableEntityType.AddProperty(string name, Type propertyType,
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- IMutableProperty IMutableEntityType.FindProperty(string name) => FindProperty(name);
+ ISkipNavigation IEntityType.FindDeclaredSkipNavigation(string name)
+ => FindDeclaredSkipNavigation(name);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2833,7 +3288,8 @@ IMutableProperty IMutableEntityType.AddProperty(string name, Type propertyType,
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- IEnumerable IEntityType.GetProperties() => GetProperties();
+ IEnumerable IEntityType.GetDeclaredSkipNavigations()
+ => GetDeclaredSkipNavigations();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2842,7 +3298,8 @@ IMutableProperty IMutableEntityType.AddProperty(string name, Type propertyType,
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- IEnumerable IMutableEntityType.GetProperties() => GetProperties();
+ IEnumerable IEntityType.GetSkipNavigations()
+ => GetSkipNavigations();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2850,7 +3307,9 @@ IMutableProperty IMutableEntityType.AddProperty(string name, Type propertyType,
/// 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.
///
- void IMutableEntityType.RemoveProperty(IMutableProperty property) => RemoveProperty((Property)property);
+ [DebuggerStepThrough]
+ IEnumerable IMutableEntityType.GetSkipNavigations()
+ => GetSkipNavigations();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2858,8 +3317,9 @@ IMutableProperty IMutableEntityType.AddProperty(string name, Type propertyType,
/// 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.
///
- IMutableServiceProperty IMutableEntityType.AddServiceProperty(MemberInfo memberInfo)
- => AddServiceProperty(memberInfo, ConfigurationSource.Explicit);
+ [DebuggerStepThrough]
+ IEnumerable IConventionEntityType.GetSkipNavigations()
+ => GetSkipNavigations();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2868,7 +3328,8 @@ IMutableServiceProperty IMutableEntityType.AddServiceProperty(MemberInfo memberI
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- IServiceProperty IEntityType.FindServiceProperty(string name) => FindServiceProperty(name);
+ void IMutableEntityType.RemoveSkipNavigation([NotNull] IMutableSkipNavigation navigation)
+ => RemoveSkipNavigation((SkipNavigation)navigation);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2877,7 +3338,8 @@ IMutableServiceProperty IMutableEntityType.AddServiceProperty(MemberInfo memberI
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- IMutableServiceProperty IMutableEntityType.FindServiceProperty(string name) => FindServiceProperty(name);
+ void IConventionEntityType.RemoveSkipNavigation([NotNull] IConventionSkipNavigation navigation)
+ => RemoveSkipNavigation((SkipNavigation)navigation);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2885,7 +3347,9 @@ IMutableServiceProperty IMutableEntityType.AddServiceProperty(MemberInfo memberI
/// 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.
///
- IEnumerable IEntityType.GetServiceProperties() => GetServiceProperties();
+ [DebuggerStepThrough]
+ IMutableIndex IMutableEntityType.AddIndex(IReadOnlyList properties)
+ => AddIndex(properties.Cast().ToList(), ConfigurationSource.Explicit);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2893,7 +3357,11 @@ IMutableServiceProperty IMutableEntityType.AddServiceProperty(MemberInfo memberI
/// 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.
///
- IEnumerable IMutableEntityType.GetServiceProperties() => GetServiceProperties();
+ [DebuggerStepThrough]
+ IConventionIndex IConventionEntityType.AddIndex(IReadOnlyList properties, bool fromDataAnnotation)
+ => AddIndex(
+ properties.Cast().ToList(),
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2901,7 +3369,8 @@ IMutableServiceProperty IMutableEntityType.AddServiceProperty(MemberInfo memberI
/// 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.
///
- IMutableServiceProperty IMutableEntityType.RemoveServiceProperty(string name) => RemoveServiceProperty(name);
+ [DebuggerStepThrough]
+ IIndex IEntityType.FindIndex(IReadOnlyList properties) => FindIndex(properties);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2909,10 +3378,8 @@ IMutableServiceProperty IMutableEntityType.AddServiceProperty(MemberInfo memberI
/// 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.
///
- IConventionEntityTypeBuilder IConventionEntityType.Builder
- {
- [DebuggerStepThrough] get => Builder;
- }
+ [DebuggerStepThrough]
+ IMutableIndex IMutableEntityType.FindIndex(IReadOnlyList properties) => FindIndex(properties);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2920,10 +3387,8 @@ IConventionEntityTypeBuilder IConventionEntityType.Builder
/// 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.
///
- IConventionModel IConventionEntityType.Model
- {
- [DebuggerStepThrough] get => Model;
- }
+ [DebuggerStepThrough]
+ IConventionIndex IConventionEntityType.FindIndex(IReadOnlyList properties) => FindIndex(properties);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2931,10 +3396,8 @@ IConventionModel IConventionEntityType.Model
/// 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.
///
- IConventionEntityType IConventionEntityType.BaseType
- {
- [DebuggerStepThrough] get => BaseType;
- }
+ [DebuggerStepThrough]
+ IEnumerable IEntityType.GetIndexes() => GetIndexes();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2942,9 +3405,8 @@ IConventionEntityType IConventionEntityType.BaseType
/// 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.
///
- void IConventionEntityType.HasBaseType(IConventionEntityType entityType, bool fromDataAnnotation)
- => HasBaseType(
- (EntityType)entityType, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+ [DebuggerStepThrough]
+ IEnumerable IMutableEntityType.GetIndexes() => GetIndexes();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2952,8 +3414,8 @@ void IConventionEntityType.HasBaseType(IConventionEntityType entityType, bool fr
/// 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.
///
- void IConventionEntityType.HasNoKey(bool? keyless, bool fromDataAnnotation)
- => HasNoKey(keyless, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+ [DebuggerStepThrough]
+ IEnumerable IConventionEntityType.GetIndexes() => GetIndexes();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2961,10 +3423,8 @@ void IConventionEntityType.HasNoKey(bool? keyless, bool fromDataAnnotation)
/// 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.
///
- IConventionKey IConventionEntityType.SetPrimaryKey(IReadOnlyList properties, bool fromDataAnnotation)
- => SetPrimaryKey(
- properties?.Cast().ToList(),
- fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+ [DebuggerStepThrough]
+ void IMutableEntityType.RemoveIndex(IMutableIndex index) => RemoveIndex((Index)index);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2972,7 +3432,8 @@ IConventionKey IConventionEntityType.SetPrimaryKey(IReadOnlyList
- IConventionKey IConventionEntityType.FindPrimaryKey() => FindPrimaryKey();
+ [DebuggerStepThrough]
+ void IConventionEntityType.RemoveIndex(IConventionIndex index) => RemoveIndex((Index)index);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2980,7 +3441,9 @@ IConventionKey IConventionEntityType.SetPrimaryKey(IReadOnlyList
- IConventionKey IConventionEntityType.FindKey(IReadOnlyList properties) => FindKey(properties);
+ [DebuggerStepThrough]
+ IMutableProperty IMutableEntityType.AddProperty(string name, Type propertyType, MemberInfo memberInfo)
+ => AddProperty(name, propertyType, memberInfo, ConfigurationSource.Explicit, ConfigurationSource.Explicit);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2988,7 +3451,17 @@ IConventionKey IConventionEntityType.SetPrimaryKey(IReadOnlyList
- IEnumerable IConventionEntityType.GetKeys() => GetKeys();
+ [DebuggerStepThrough]
+ IConventionProperty IConventionEntityType.AddProperty(
+ string name, Type propertyType, MemberInfo memberInfo, bool setTypeConfigurationSource, bool fromDataAnnotation)
+ => AddProperty(
+ name,
+ propertyType,
+ memberInfo,
+ setTypeConfigurationSource
+ ? fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention
+ : (ConfigurationSource?)null,
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -2996,9 +3469,8 @@ IConventionKey IConventionEntityType.SetPrimaryKey(IReadOnlyList
- IConventionForeignKey IConventionEntityType.FindForeignKey(
- IReadOnlyList properties, IKey principalKey, IEntityType principalEntityType)
- => FindForeignKey(properties, principalKey, principalEntityType);
+ [DebuggerStepThrough]
+ IProperty IEntityType.FindProperty(string name) => FindProperty(name);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3006,7 +3478,8 @@ IConventionForeignKey IConventionEntityType.FindForeignKey(
/// 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.
///
- IEnumerable IConventionEntityType.GetForeignKeys() => GetForeignKeys();
+ [DebuggerStepThrough]
+ IMutableProperty IMutableEntityType.FindProperty(string name) => FindProperty(name);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3014,7 +3487,8 @@ IConventionForeignKey IConventionEntityType.FindForeignKey(
/// 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.
///
- IConventionIndex IConventionEntityType.FindIndex(IReadOnlyList properties) => FindIndex(properties);
+ [DebuggerStepThrough]
+ IConventionProperty IConventionEntityType.FindProperty(string name) => FindProperty(name);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3022,7 +3496,8 @@ IConventionForeignKey IConventionEntityType.FindForeignKey(
/// 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.
///
- IEnumerable IConventionEntityType.GetIndexes() => GetIndexes();
+ [DebuggerStepThrough]
+ IEnumerable IEntityType.GetProperties() => GetProperties();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3030,7 +3505,8 @@ IConventionForeignKey IConventionEntityType.FindForeignKey(
/// 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.
///
- IConventionProperty IConventionEntityType.FindProperty(string name) => FindProperty(name);
+ [DebuggerStepThrough]
+ IEnumerable IMutableEntityType.GetProperties() => GetProperties();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3038,6 +3514,7 @@ IConventionForeignKey IConventionEntityType.FindForeignKey(
/// 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.
///
+ [DebuggerStepThrough]
IEnumerable IConventionEntityType.GetProperties() => GetProperties();
///
@@ -3046,7 +3523,8 @@ IConventionForeignKey IConventionEntityType.FindForeignKey(
/// 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.
///
- IConventionServiceProperty IConventionEntityType.FindServiceProperty(string name) => FindServiceProperty(name);
+ [DebuggerStepThrough]
+ void IMutableEntityType.RemoveProperty(IMutableProperty property) => RemoveProperty((Property)property);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3054,7 +3532,8 @@ IConventionForeignKey IConventionEntityType.FindForeignKey(
/// 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.
///
- IEnumerable IConventionEntityType.GetServiceProperties() => GetServiceProperties();
+ [DebuggerStepThrough]
+ void IConventionEntityType.RemoveProperty(IConventionProperty property) => RemoveProperty((Property)property);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3062,7 +3541,9 @@ IConventionForeignKey IConventionEntityType.FindForeignKey(
/// 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.
///
- IConventionServiceProperty IConventionEntityType.RemoveServiceProperty(string name) => RemoveServiceProperty(name);
+ [DebuggerStepThrough]
+ IMutableServiceProperty IMutableEntityType.AddServiceProperty(MemberInfo memberInfo)
+ => AddServiceProperty(memberInfo, ConfigurationSource.Explicit);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3070,7 +3551,9 @@ IConventionForeignKey IConventionEntityType.FindForeignKey(
/// 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.
///
- void IConventionEntityType.RemoveProperty(IConventionProperty property) => RemoveProperty((Property)property);
+ [DebuggerStepThrough]
+ IConventionServiceProperty IConventionEntityType.AddServiceProperty(MemberInfo memberInfo, bool fromDataAnnotation)
+ => AddServiceProperty(memberInfo, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3078,8 +3561,8 @@ IConventionForeignKey IConventionEntityType.FindForeignKey(
/// 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.
///
- IConventionServiceProperty IConventionEntityType.AddServiceProperty(MemberInfo memberInfo, bool fromDataAnnotation)
- => AddServiceProperty(memberInfo, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+ [DebuggerStepThrough]
+ IServiceProperty IEntityType.FindServiceProperty(string name) => FindServiceProperty(name);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3087,7 +3570,8 @@ IConventionServiceProperty IConventionEntityType.AddServiceProperty(MemberInfo m
/// 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.
///
- void IConventionEntityType.RemoveIndex(IConventionIndex index) => RemoveIndex((Index)index);
+ [DebuggerStepThrough]
+ IMutableServiceProperty IMutableEntityType.FindServiceProperty(string name) => FindServiceProperty(name);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3095,16 +3579,8 @@ IConventionServiceProperty IConventionEntityType.AddServiceProperty(MemberInfo m
/// 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.
///
- IConventionProperty IConventionEntityType.AddProperty(
- string name, Type propertyType, MemberInfo memberInfo, bool setTypeConfigurationSource, bool fromDataAnnotation)
- => AddProperty(
- name,
- propertyType,
- memberInfo,
- setTypeConfigurationSource
- ? fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention
- : (ConfigurationSource?)null,
- fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+ [DebuggerStepThrough]
+ IConventionServiceProperty IConventionEntityType.FindServiceProperty(string name) => FindServiceProperty(name);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3112,8 +3588,8 @@ IConventionProperty IConventionEntityType.AddProperty(
/// 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.
///
- void IConventionEntityType.RemoveForeignKey(IConventionForeignKey foreignKey)
- => RemoveForeignKey((ForeignKey)foreignKey);
+ [DebuggerStepThrough]
+ IEnumerable IEntityType.GetServiceProperties() => GetServiceProperties();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3121,10 +3597,8 @@ void IConventionEntityType.RemoveForeignKey(IConventionForeignKey foreignKey)
/// 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.
///
- IConventionIndex IConventionEntityType.AddIndex(IReadOnlyList properties, bool fromDataAnnotation)
- => AddIndex(
- properties.Cast().ToList(),
- fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+ [DebuggerStepThrough]
+ IEnumerable IMutableEntityType.GetServiceProperties() => GetServiceProperties();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3132,7 +3606,8 @@ IConventionIndex IConventionEntityType.AddIndex(IReadOnlyList
- void IConventionEntityType.RemoveKey(IConventionKey key) => RemoveKey((Key)key);
+ [DebuggerStepThrough]
+ IEnumerable IConventionEntityType.GetServiceProperties() => GetServiceProperties();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3140,20 +3615,8 @@ IConventionIndex IConventionEntityType.AddIndex(IReadOnlyList
- IConventionForeignKey IConventionEntityType.AddForeignKey(
- IReadOnlyList properties,
- IConventionKey principalKey,
- IConventionEntityType principalEntityType,
- bool setComponentConfigurationSource,
- bool fromDataAnnotation)
- => AddForeignKey(
- properties.Cast().ToList(),
- (Key)principalKey,
- (EntityType)principalEntityType,
- setComponentConfigurationSource
- ? fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention
- : (ConfigurationSource?)null,
- fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+ [DebuggerStepThrough]
+ IMutableServiceProperty IMutableEntityType.RemoveServiceProperty(string name) => RemoveServiceProperty(name);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -3161,10 +3624,8 @@ IConventionForeignKey IConventionEntityType.AddForeignKey(
/// 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.
///
- IConventionKey IConventionEntityType.AddKey(IReadOnlyList properties, bool fromDataAnnotation)
- => AddKey(
- properties.Cast().ToList(),
- fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+ [DebuggerStepThrough]
+ IConventionServiceProperty IConventionEntityType.RemoveServiceProperty(string name) => RemoveServiceProperty(name);
#endregion
@@ -3174,64 +3635,6 @@ private static IEnumerable ToEnumerable(T element)
? Enumerable.Empty()
: new[] { element };
- private sealed class PropertyComparer : IComparer
- {
- private readonly EntityType _entityType;
-
- public PropertyComparer(EntityType entityType)
- {
- _entityType = entityType;
- }
-
- public int Compare(string x, string y)
- {
- var xIndex = -1;
- var yIndex = -1;
-
- var properties = _entityType.FindPrimaryKey()?.Properties;
-
- if (properties != null)
- {
- for (var i = 0; i < properties.Count; i++)
- {
- var name = properties[i].Name;
-
- if (name == x)
- {
- xIndex = i;
- }
-
- if (name == y)
- {
- yIndex = i;
- }
- }
- }
-
- // Neither property is part of the Primary Key
- // Compare the property names
- if (xIndex == -1
- && yIndex == -1)
- {
- return StringComparer.Ordinal.Compare(x, y);
- }
-
- // Both properties are part of the Primary Key
- // Compare the indices
- if (xIndex > -1
- && yIndex > -1)
- {
- return xIndex - yIndex;
- }
-
- // One property is part of the Primary Key
- // The primary key property is first
- return xIndex > yIndex
- ? -1
- : 1;
- }
- }
-
///
/// 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
diff --git a/src/EFCore/Metadata/Internal/ForeignKey.cs b/src/EFCore/Metadata/Internal/ForeignKey.cs
index 504227bb798..7559aaac65a 100644
--- a/src/EFCore/Metadata/Internal/ForeignKey.cs
+++ b/src/EFCore/Metadata/Internal/ForeignKey.cs
@@ -83,7 +83,7 @@ public ForeignKey(
/// 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.
///
- public virtual IReadOnlyList Properties { [DebuggerStepThrough] get; private set; }
+ public virtual IReadOnlyList Properties { get; private set; }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -91,7 +91,7 @@ public ForeignKey(
/// 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.
///
- public virtual Key PrincipalKey { [DebuggerStepThrough] get; private set; }
+ public virtual Key PrincipalKey { get; private set; }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -99,7 +99,7 @@ public ForeignKey(
/// 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.
///
- public virtual EntityType DeclaringEntityType { [DebuggerStepThrough] get; }
+ public virtual EntityType DeclaringEntityType { get; }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -107,7 +107,7 @@ public ForeignKey(
/// 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.
///
- public virtual EntityType PrincipalEntityType { [DebuggerStepThrough] get; }
+ public virtual EntityType PrincipalEntityType { get; }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -117,12 +117,29 @@ public ForeignKey(
///
public virtual InternalRelationshipBuilder Builder
{
- [DebuggerStepThrough] get;
- [DebuggerStepThrough]
+ get;
+
[param: CanBeNull]
set;
}
+ ///
+ /// 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.
+ ///
+ public virtual SortedSet ReferencingSkipNavigations { get; [param: CanBeNull] set; }
+
+ ///
+ /// 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.
+ ///
+ public virtual IEnumerable GetReferencingSkipNavigations()
+ => ReferencingSkipNavigations ?? Enumerable.Empty();
+
///
/// 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
@@ -979,6 +996,14 @@ IConventionNavigation IConventionForeignKey.PrincipalToDependent
[DebuggerStepThrough] get => PrincipalToDependent;
}
+ ///
+ /// 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.
+ ///
+ IConventionRelationshipBuilder IConventionForeignKey.Builder => Builder;
+
///
/// 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
@@ -1065,6 +1090,17 @@ void IConventionForeignKey.SetIsOwnership(bool? ownership, bool fromDataAnnotati
void IConventionForeignKey.SetDeleteBehavior(DeleteBehavior? deleteBehavior, bool fromDataAnnotation)
=> SetDeleteBehavior(deleteBehavior, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+ ///
+ /// 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.
+ ///
+ public virtual DebugView DebugView
+ => new DebugView(
+ () => this.ToDebugString(MetadataDebugStringOptions.ShortDefault),
+ () => this.ToDebugString(MetadataDebugStringOptions.LongDefault));
+
///
/// 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
@@ -1253,24 +1289,5 @@ private static bool ArePropertyTypesCompatible(
IReadOnlyList principalProperties, IReadOnlyList dependentProperties)
=> principalProperties.Select(p => p.ClrType.UnwrapNullableType()).SequenceEqual(
dependentProperties.Select(p => p.ClrType.UnwrapNullableType()));
-
- ///
- /// 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.
- ///
- public virtual DebugView DebugView
- => new DebugView(
- () => this.ToDebugString(MetadataDebugStringOptions.ShortDefault),
- () => this.ToDebugString(MetadataDebugStringOptions.LongDefault));
-
- ///
- /// 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.
- ///
- IConventionRelationshipBuilder IConventionForeignKey.Builder => Builder;
}
}
diff --git a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
index fa706cad370..6aabdf9f7ba 100644
--- a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
+++ b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
@@ -119,7 +119,7 @@ public virtual InternalKeyBuilder PrimaryKey(
}
}
- // TODO: Use convention batch to get the updated builder, see #214
+ // TODO: Use convention batch to get the updated builder, see #15898
if (keyBuilder?.Metadata.Builder == null)
{
properties = GetActualProperties(properties, null);
@@ -290,7 +290,7 @@ public virtual InternalEntityTypeBuilder HasNoKey([NotNull] Key key, Configurati
&& property.ClrType.IsNullableType()
&& !property.GetContainingForeignKeys().Any(fk => fk.IsRequired))
{
- // TODO: This should be handled by reference tracking, see #214
+ // TODO: This should be handled by reference tracking, see #15898
property.Builder?.IsRequired(null, configurationSource);
}
}
diff --git a/src/EFCore/Metadata/Internal/InternalSkipNavigationBuilder.cs b/src/EFCore/Metadata/Internal/InternalSkipNavigationBuilder.cs
new file mode 100644
index 00000000000..b4eaf47054a
--- /dev/null
+++ b/src/EFCore/Metadata/Internal/InternalSkipNavigationBuilder.cs
@@ -0,0 +1,35 @@
+// 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.Diagnostics;
+using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Internal
+{
+ ///
+ /// 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.
+ ///
+ public class InternalSkipNavigationBuilder : InternalModelItemBuilder, IConventionSkipNavigationBuilder
+ {
+ ///
+ /// 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.
+ ///
+ public InternalSkipNavigationBuilder([NotNull] SkipNavigation metadata, [NotNull] InternalModelBuilder modelBuilder)
+ : base(metadata, modelBuilder)
+ {
+ }
+
+ IConventionSkipNavigation IConventionSkipNavigationBuilder.Metadata
+ {
+ [DebuggerStepThrough]
+ get => Metadata;
+ }
+ }
+}
diff --git a/src/EFCore/Metadata/Internal/Key.cs b/src/EFCore/Metadata/Internal/Key.cs
index 777c6c8faf7..19f7d1e8ef5 100644
--- a/src/EFCore/Metadata/Internal/Key.cs
+++ b/src/EFCore/Metadata/Internal/Key.cs
@@ -148,7 +148,6 @@ public virtual IPrincipalKeyValueFactory GetPrincipalKeyValueFactory
/// 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.
///
- // Note this is ISet because there is no suitable readonly interface in the profiles we are using
public virtual ISet ReferencingForeignKeys { get; [param: CanBeNull] set; }
///
diff --git a/src/EFCore/Metadata/Internal/Navigation.cs b/src/EFCore/Metadata/Internal/Navigation.cs
index dc2df28c5c2..1fe7a061292 100644
--- a/src/EFCore/Metadata/Internal/Navigation.cs
+++ b/src/EFCore/Metadata/Internal/Navigation.cs
@@ -65,13 +65,7 @@ public Navigation(
/// 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.
///
- public virtual InternalNavigationBuilder Builder
- {
- [DebuggerStepThrough] get;
- [DebuggerStepThrough]
- [param: CanBeNull]
- set;
- }
+ public virtual InternalNavigationBuilder Builder { get; [param: CanBeNull] set; }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -204,7 +198,9 @@ public static bool IsCompatible(
///
[DebuggerStepThrough]
public virtual Navigation FindInverse()
- => (Navigation)((INavigation)this).FindInverse();
+ => this.IsDependentToPrincipal()
+ ? ForeignKey.PrincipalToDependent
+ : ForeignKey.DependentToPrincipal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -214,7 +210,9 @@ public virtual Navigation FindInverse()
///
[DebuggerStepThrough]
public virtual EntityType GetTargetType()
- => (EntityType)((INavigation)this).GetTargetType();
+ => this.IsDependentToPrincipal()
+ ? ForeignKey.PrincipalEntityType
+ : ForeignKey.DeclaringEntityType;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -224,10 +222,19 @@ public virtual EntityType GetTargetType()
///
public virtual IClrCollectionAccessor CollectionAccessor
=> NonCapturingLazyInitializer.EnsureInitialized(
- ref _collectionAccessor, this, n =>
- !n.IsCollection() || n.IsShadowProperty()
- ? null
- : new ClrCollectionAccessorFactory().Create(n));
+ ref _collectionAccessor, this, n => new ClrCollectionAccessorFactory().Create(n));
+
+ ///
+ /// Runs the conventions when an annotation was set or removed.
+ ///
+ /// The key of the set annotation.
+ /// The annotation set.
+ /// The old annotation.
+ /// The annotation that was set.
+ protected override IConventionAnnotation OnAnnotationSet(
+ string name, IConventionAnnotation annotation, IConventionAnnotation oldAnnotation)
+ => DeclaringType.Model.ConventionDispatcher.OnNavigationAnnotationChanged(
+ ForeignKey.Builder, this, name, annotation, oldAnnotation);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -298,7 +305,10 @@ IMutableEntityType IMutableNavigation.DeclaringEntityType
/// 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.
///
- IConventionEntityType IConventionNavigation.DeclaringEntityType => DeclaringEntityType;
+ IConventionEntityType IConventionNavigation.DeclaringEntityType
+ {
+ [DebuggerStepThrough] get => DeclaringEntityType;
+ }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -306,6 +316,9 @@ IMutableEntityType IMutableNavigation.DeclaringEntityType
/// 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.
///
- IConventionForeignKey IConventionNavigation.ForeignKey => ForeignKey;
+ IConventionForeignKey IConventionNavigation.ForeignKey
+ {
+ [DebuggerStepThrough] get => ForeignKey;
+ }
}
}
diff --git a/src/EFCore/Metadata/Internal/Property.cs b/src/EFCore/Metadata/Internal/Property.cs
index 9098201e9b4..b1eeeb581ec 100644
--- a/src/EFCore/Metadata/Internal/Property.cs
+++ b/src/EFCore/Metadata/Internal/Property.cs
@@ -69,7 +69,7 @@ public Property(
/// 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.
///
- public virtual EntityType DeclaringEntityType { [DebuggerStepThrough] get; }
+ public virtual EntityType DeclaringEntityType { get; }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -79,7 +79,8 @@ public Property(
///
public override TypeBase DeclaringType
{
- [DebuggerStepThrough] get => DeclaringEntityType;
+ [DebuggerStepThrough]
+ get => DeclaringEntityType;
}
///
@@ -88,7 +89,7 @@ public override TypeBase DeclaringType
/// 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.
///
- public override Type ClrType { [DebuggerStepThrough] get; }
+ public override Type ClrType { get; }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -96,13 +97,7 @@ public override TypeBase DeclaringType
/// 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.
///
- public virtual InternalPropertyBuilder Builder
- {
- [DebuggerStepThrough] get;
- [DebuggerStepThrough]
- [param: CanBeNull]
- set;
- }
+ public virtual InternalPropertyBuilder Builder { get; [param: CanBeNull] set; }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -126,7 +121,7 @@ public virtual bool UpdateConfigurationSource(ConfigurationSource configurationS
}
// Needed for a workaround before reference counting is implemented
- // Issue #214
+ // Issue #15898
///
/// 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
@@ -532,8 +527,15 @@ public virtual string CheckValueComparer([CanBeNull] ValueComparer comparer)
/// 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.
///
- public virtual IEnumerable GetContainingForeignKeys()
- => ForeignKeys ?? Enumerable.Empty();
+ public virtual IKey PrimaryKey { get; [param: CanBeNull] set; }
+
+ ///
+ /// 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.
+ ///
+ public virtual List Keys { get; [param: CanBeNull] set; }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -544,6 +546,31 @@ public virtual IEnumerable GetContainingForeignKeys()
public virtual IEnumerable GetContainingKeys()
=> Keys ?? Enumerable.Empty();
+ ///
+ /// 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.
+ ///
+ public virtual List ForeignKeys { get; [param: CanBeNull] set; }
+
+ ///
+ /// 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.
+ ///
+ public virtual IEnumerable GetContainingForeignKeys()
+ => ForeignKeys ?? Enumerable.Empty();
+
+ ///
+ /// 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.
+ ///
+ public virtual List Indexes { get; [param: CanBeNull] set; }
+
///
/// 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
@@ -598,38 +625,6 @@ public static bool AreCompatible([NotNull] IReadOnlyList properties, [
&& entityType.GetRuntimeFields().ContainsKey(property.Name)))));
}
- ///
- /// 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.
- ///
- public virtual IKey PrimaryKey { get; [param: CanBeNull] set; }
-
- ///
- /// 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.
- ///
- public virtual List Keys { get; [param: CanBeNull] set; }
-
- ///
- /// 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.
- ///
- public virtual List ForeignKeys { get; [param: CanBeNull] set; }
-
- ///
- /// 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.
- ///
- public virtual List Indexes { get; [param: CanBeNull] set; }
-
///
/// 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
diff --git a/src/EFCore/Metadata/Internal/PropertyBase.cs b/src/EFCore/Metadata/Internal/PropertyBase.cs
index d27a4e21b5f..5e9a7e1e597 100644
--- a/src/EFCore/Metadata/Internal/PropertyBase.cs
+++ b/src/EFCore/Metadata/Internal/PropertyBase.cs
@@ -183,7 +183,7 @@ public virtual void SetPropertyAccessMode(PropertyAccessMode? propertyAccessMode
///
public static bool IsCompatible(
[NotNull] FieldInfo fieldInfo,
- [NotNull] Type propertyType,
+ [CanBeNull] Type propertyType,
[CanBeNull] Type entityClrType,
[CanBeNull] string propertyName,
bool shouldThrow)
@@ -203,7 +203,8 @@ public static bool IsCompatible(
}
var fieldTypeInfo = fieldInfo.FieldType;
- if (!fieldTypeInfo.IsAssignableFrom(propertyType)
+ if (propertyType != null
+ && !fieldTypeInfo.IsAssignableFrom(propertyType)
&& !propertyType.IsAssignableFrom(fieldTypeInfo))
{
if (shouldThrow)
diff --git a/src/EFCore/Metadata/Internal/PropertyNameComparer.cs b/src/EFCore/Metadata/Internal/PropertyNameComparer.cs
new file mode 100644
index 00000000000..9c740341b2a
--- /dev/null
+++ b/src/EFCore/Metadata/Internal/PropertyNameComparer.cs
@@ -0,0 +1,73 @@
+// 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.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Internal
+{
+ ///
+ /// 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.
+ ///
+ public sealed class PropertyNameComparer : IComparer
+ {
+ private readonly EntityType _entityType;
+
+ public PropertyNameComparer([NotNull] EntityType entityType)
+ {
+ _entityType = entityType;
+ }
+
+ public int Compare(string x, string y)
+ {
+ var xIndex = -1;
+ var yIndex = -1;
+
+ var properties = _entityType.FindPrimaryKey()?.Properties;
+
+ if (properties != null)
+ {
+ for (var i = 0; i < properties.Count; i++)
+ {
+ var name = properties[i].Name;
+
+ if (name == x)
+ {
+ xIndex = i;
+ }
+
+ if (name == y)
+ {
+ yIndex = i;
+ }
+ }
+ }
+
+ // Neither property is part of the Primary Key
+ // Compare the property names
+ if (xIndex == -1
+ && yIndex == -1)
+ {
+ return StringComparer.Ordinal.Compare(x, y);
+ }
+
+ // Both properties are part of the Primary Key
+ // Compare the indices
+ if (xIndex > -1
+ && yIndex > -1)
+ {
+ return xIndex - yIndex;
+ }
+
+ // One property is part of the Primary Key
+ // The primary key property is first
+ return xIndex > yIndex
+ ? -1
+ : 1;
+ }
+ }
+}
diff --git a/src/EFCore/Metadata/Internal/SkipNavigation.cs b/src/EFCore/Metadata/Internal/SkipNavigation.cs
new file mode 100644
index 00000000000..793914a7fa0
--- /dev/null
+++ b/src/EFCore/Metadata/Internal/SkipNavigation.cs
@@ -0,0 +1,317 @@
+// 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.Diagnostics;
+using System.Reflection;
+using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Internal;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Microsoft.EntityFrameworkCore.Utilities;
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Internal
+{
+ ///
+ /// 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.
+ ///
+ public class SkipNavigation : PropertyBase, IMutableSkipNavigation, IConventionSkipNavigation
+ {
+ private ConfigurationSource _configurationSource;
+ private ConfigurationSource? _inverseConfigurationSource;
+
+ // Warning: Never access these fields directly as access needs to be thread-safe
+ private IClrCollectionAccessor _collectionAccessor;
+
+ ///
+ /// 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.
+ ///
+ public SkipNavigation(
+ [NotNull] string name,
+ [CanBeNull] PropertyInfo propertyInfo,
+ [CanBeNull] FieldInfo fieldInfo,
+ [NotNull] EntityType targetEntityType,
+ [NotNull] ForeignKey foreignKey,
+ bool collection,
+ bool onPrincipal,
+ ConfigurationSource configurationSource)
+ : base(name, propertyInfo, fieldInfo)
+ {
+ Check.NotNull(foreignKey, nameof(foreignKey));
+
+ TargetEntityType = targetEntityType;
+ ForeignKey = foreignKey;
+ IsCollection = collection;
+ IsOnPrincipal = onPrincipal;
+ _configurationSource = configurationSource;
+ Builder = new InternalSkipNavigationBuilder(this, targetEntityType.Model.Builder);
+ }
+
+ ///
+ /// 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.
+ ///
+ public override Type ClrType => this.GetIdentifyingMemberInfo()?.GetMemberType();
+
+ ///
+ /// 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.
+ ///
+ public virtual ForeignKey ForeignKey { get; }
+
+ ///
+ /// 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.
+ ///
+ public virtual InternalSkipNavigationBuilder Builder { get; [param: CanBeNull] set; }
+
+ ///
+ /// 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.
+ ///
+ public override TypeBase DeclaringType => IsOnPrincipal ? ForeignKey.PrincipalEntityType : ForeignKey.DeclaringEntityType;
+
+ ///
+ /// 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.
+ ///
+ public virtual EntityType AssociationEntityType => IsOnPrincipal ? ForeignKey.DeclaringEntityType : ForeignKey.PrincipalEntityType;
+
+ ///
+ /// 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.
+ ///
+ public virtual EntityType TargetEntityType { get; }
+
+ ///
+ /// 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.
+ ///
+ public virtual SkipNavigation Inverse { get; [param: NotNull] private set; }
+
+ ///
+ /// 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.
+ ///
+ public virtual bool IsCollection { get; }
+
+ ///
+ /// 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.
+ ///
+ public virtual bool IsOnPrincipal { get; }
+
+ ///
+ /// 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.
+ ///
+ public virtual ConfigurationSource GetConfigurationSource() => _configurationSource;
+
+ ///
+ /// 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.
+ ///
+ public virtual bool UpdateConfigurationSource(ConfigurationSource configurationSource)
+ {
+ var oldConfigurationSource = _configurationSource;
+ _configurationSource = configurationSource.Max(_configurationSource);
+ return _configurationSource != oldConfigurationSource;
+ }
+
+ ///
+ /// 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.
+ ///
+ public virtual IConventionSkipNavigation SetInverse([CanBeNull] SkipNavigation inverse, ConfigurationSource configurationSource)
+ {
+ var oldInverse = Inverse;
+ var isChanging = inverse != Inverse;
+ if (inverse == null)
+ {
+ Inverse = null;
+ _inverseConfigurationSource = null;
+
+ return isChanging
+ ? DeclaringType.Model.ConventionDispatcher.OnSkipNavigationInverseChanged(Builder, inverse, oldInverse)
+ : inverse;
+ }
+
+ if (inverse.DeclaringType != TargetEntityType)
+ {
+ throw new InvalidOperationException(CoreStrings.SkipNavigationWrongInverse(
+ inverse.Name, inverse.DeclaringType.DisplayName(), Name, TargetEntityType.DisplayName()));
+ }
+
+ if (inverse.AssociationEntityType != AssociationEntityType)
+ {
+ throw new InvalidOperationException(CoreStrings.SkipInverseMismatchedAssociationType(
+ inverse.Name, inverse.AssociationEntityType.DisplayName(), Name, AssociationEntityType.DisplayName()));
+ }
+
+ Inverse = inverse;
+ UpdateInverseConfigurationSource(configurationSource);
+
+ return isChanging
+ ? DeclaringType.Model.ConventionDispatcher.OnSkipNavigationInverseChanged(Builder, inverse, oldInverse)
+ : inverse;
+ }
+
+ ///
+ /// 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.
+ ///
+ public virtual ConfigurationSource? GetInverseConfigurationSource()
+ => _inverseConfigurationSource;
+
+ ///
+ /// 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.
+ ///
+ public virtual void UpdateInverseConfigurationSource(ConfigurationSource configurationSource)
+ => _inverseConfigurationSource = _inverseConfigurationSource.Max(configurationSource);
+
+ ///
+ /// Runs the conventions when an annotation was set or removed.
+ ///
+ /// The key of the set annotation.
+ /// The annotation set.
+ /// The old annotation.
+ /// The annotation that was set.
+ protected override IConventionAnnotation OnAnnotationSet(
+ string name, IConventionAnnotation annotation, IConventionAnnotation oldAnnotation)
+ => DeclaringType.Model.ConventionDispatcher.OnSkipNavigationAnnotationChanged(
+ Builder, name, annotation, oldAnnotation);
+
+ ///
+ /// 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.
+ ///
+ public virtual IClrCollectionAccessor CollectionAccessor
+ => NonCapturingLazyInitializer.EnsureInitialized(
+ ref _collectionAccessor, this, n => new ClrCollectionAccessorFactory().Create(n));
+
+ ///
+ /// 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.
+ ///
+ public virtual DebugView DebugView
+ => new DebugView(
+ () => this.ToDebugString(MetadataDebugStringOptions.ShortDefault),
+ () => this.ToDebugString(MetadataDebugStringOptions.LongDefault));
+
+ ///
+ /// 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.
+ ///
+ [DebuggerStepThrough]
+ public override string ToString() => this.ToDebugString(MetadataDebugStringOptions.SingleLineDefault);
+
+ IConventionSkipNavigationBuilder IConventionSkipNavigation.Builder
+ {
+ [DebuggerStepThrough]
+ get => Builder;
+ }
+
+ IEntityType ISkipNavigation.TargetEntityType
+ {
+ [DebuggerStepThrough]
+ get => TargetEntityType;
+ }
+
+ IMutableEntityType IMutableSkipNavigation.TargetEntityType
+ {
+ [DebuggerStepThrough]
+ get => TargetEntityType;
+ }
+
+ IConventionEntityType IConventionSkipNavigation.TargetEntityType
+ {
+ [DebuggerStepThrough]
+ get => TargetEntityType;
+ }
+
+ IForeignKey ISkipNavigation.ForeignKey
+ {
+ [DebuggerStepThrough]
+ get => ForeignKey;
+ }
+
+ IMutableForeignKey IMutableSkipNavigation.ForeignKey
+ {
+ [DebuggerStepThrough]
+ get => ForeignKey;
+ }
+
+ IConventionForeignKey IConventionSkipNavigation.ForeignKey
+ {
+ [DebuggerStepThrough]
+ get => ForeignKey;
+ }
+
+ ISkipNavigation ISkipNavigation.Inverse
+ {
+ [DebuggerStepThrough]
+ get => Inverse;
+ }
+
+ IMutableSkipNavigation IMutableSkipNavigation.Inverse
+ {
+ [DebuggerStepThrough]
+ get => Inverse;
+ }
+
+ IConventionSkipNavigation IConventionSkipNavigation.Inverse
+ {
+ [DebuggerStepThrough]
+ get => Inverse;
+ }
+
+ [DebuggerStepThrough]
+ IConventionSkipNavigation IMutableSkipNavigation.SetInverse([CanBeNull] IMutableSkipNavigation inverse)
+ => SetInverse((SkipNavigation)inverse, ConfigurationSource.Explicit);
+
+ [DebuggerStepThrough]
+ IConventionSkipNavigation IConventionSkipNavigation.SetInverse([CanBeNull] IConventionSkipNavigation inverse, bool fromDataAnnotation)
+ => SetInverse((SkipNavigation)inverse, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+ }
+}
diff --git a/src/EFCore/Metadata/Internal/SkipNavigationComparer.cs b/src/EFCore/Metadata/Internal/SkipNavigationComparer.cs
new file mode 100644
index 00000000000..40e9472cb29
--- /dev/null
+++ b/src/EFCore/Metadata/Internal/SkipNavigationComparer.cs
@@ -0,0 +1,32 @@
+// 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.Collections.Generic;
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Internal
+{
+ ///
+ /// 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.
+ ///
+ public sealed class SkipNavigationComparer : IComparer
+ {
+ private SkipNavigationComparer()
+ {
+ }
+
+ ///
+ /// 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.
+ ///
+ public static readonly SkipNavigationComparer Instance = new SkipNavigationComparer();
+
+ public int Compare(SkipNavigation x, SkipNavigation y)
+ => StringComparer.Ordinal.Compare(x.Name, y.Name);
+ }
+}
diff --git a/src/EFCore/Metadata/Internal/SkipNavigationExtensions.cs b/src/EFCore/Metadata/Internal/SkipNavigationExtensions.cs
new file mode 100644
index 00000000000..c975a4137da
--- /dev/null
+++ b/src/EFCore/Metadata/Internal/SkipNavigationExtensions.cs
@@ -0,0 +1,95 @@
+// 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.Text;
+using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Infrastructure.Internal;
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Internal
+{
+ ///
+ /// 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.
+ ///
+ public static class SkipNavigationExtensions
+ {
+ ///
+ /// 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.
+ ///
+ public static string ToDebugString(
+ [NotNull] this ISkipNavigation navigation,
+ MetadataDebugStringOptions options,
+ [NotNull] string indent = "")
+ {
+ var builder = new StringBuilder();
+
+ builder.Append(indent);
+
+ var singleLine = (options & MetadataDebugStringOptions.SingleLine) != 0;
+ if (singleLine)
+ {
+ builder.Append($"SkipNavigation: {navigation.DeclaringEntityType.DisplayName()}.");
+ }
+
+ builder.Append(navigation.Name);
+
+ var field = navigation.GetFieldName();
+ if (field == null)
+ {
+ builder.Append(" (no field, ");
+ }
+ else if (!field.EndsWith(">k__BackingField", StringComparison.Ordinal))
+ {
+ builder.Append($" ({field}, ");
+ }
+ else
+ {
+ builder.Append(" (");
+ }
+
+ builder.Append(navigation.ClrType?.ShortDisplayName()).Append(")");
+
+ if (navigation.IsCollection)
+ {
+ builder.Append(" Collection");
+ }
+
+ builder.Append(navigation.TargetEntityType.DisplayName());
+
+ if (navigation.Inverse != null)
+ {
+ builder.Append(" Inverse: ").Append(navigation.Inverse.Name);
+ }
+
+ if (navigation.GetPropertyAccessMode() != PropertyAccessMode.PreferField)
+ {
+ builder.Append(" PropertyAccessMode.").Append(navigation.GetPropertyAccessMode());
+ }
+
+ if ((options & MetadataDebugStringOptions.IncludePropertyIndexes) != 0)
+ {
+ var indexes = navigation.GetPropertyIndexes();
+ builder.Append(" ").Append(indexes.Index);
+ builder.Append(" ").Append(indexes.OriginalValueIndex);
+ builder.Append(" ").Append(indexes.RelationshipIndex);
+ builder.Append(" ").Append(indexes.ShadowIndex);
+ builder.Append(" ").Append(indexes.StoreGenerationIndex);
+ }
+
+ if (!singleLine &&
+ (options & MetadataDebugStringOptions.IncludeAnnotations) != 0)
+ {
+ builder.Append(navigation.AnnotationsToDebugString(indent + " "));
+ }
+
+ return builder.ToString();
+ }
+ }
+}
diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs
index 677a7dfa10c..85f83f0a9b1 100644
--- a/src/EFCore/Properties/CoreStrings.Designer.cs
+++ b/src/EFCore/Properties/CoreStrings.Designer.cs
@@ -545,12 +545,12 @@ public static string PropertyInUseKey([CanBeNull] object property, [CanBeNull] o
property, entityType, key);
///
- /// Cannot remove key {key} from entity type '{entityType}' because it is referenced by a foreign key in entity type '{dependentType}'. All foreign keys must be removed or redefined before the referenced key can be removed.
+ /// Cannot remove key {key} from entity type '{entityType}' because it is referenced by a foreign key {foreignKey} in entity type '{dependentType}'. All foreign keys must be removed or redefined before the referenced key can be removed.
///
- public static string KeyInUse([CanBeNull] object key, [CanBeNull] object entityType, [CanBeNull] object dependentType)
+ public static string KeyInUse([CanBeNull] object key, [CanBeNull] object entityType, [CanBeNull] object foreignKey, [CanBeNull] object dependentType)
=> string.Format(
- GetString("KeyInUse", nameof(key), nameof(entityType), nameof(dependentType)),
- key, entityType, dependentType);
+ GetString("KeyInUse", nameof(key), nameof(entityType), nameof(foreignKey), nameof(dependentType)),
+ key, entityType, foreignKey, dependentType);
///
/// The service property '{property}' of type '{serviceType}' cannot be added to the entity type '{entityType}' because service property '{duplicateName}' of the same type already exists on entity type '{duplicateEntityType}'.
@@ -2097,36 +2097,36 @@ public static string NoDiscriminatorValue([CanBeNull] object entityType)
entityType);
///
- /// The foreign key {foreignKey} targeting the key {key} on '{principalType}' is defined on the entity type '{otherEntityType}', not '{entityType}'.
+ /// The foreign key {foreignKey} targeting the key {key} on '{principalType}' cannot be removed from the entity type '{entityType}' because it is defined on the entity type '{otherEntityType}'.
///
- public static string ForeignKeyWrongType([CanBeNull] object foreignKey, [CanBeNull] object key, [CanBeNull] object principalType, [CanBeNull] object otherEntityType, [CanBeNull] object entityType)
+ public static string ForeignKeyWrongType([CanBeNull] object foreignKey, [CanBeNull] object key, [CanBeNull] object principalType, [CanBeNull] object entityType, [CanBeNull] object otherEntityType)
=> string.Format(
- GetString("ForeignKeyWrongType", nameof(foreignKey), nameof(key), nameof(principalType), nameof(otherEntityType), nameof(entityType)),
- foreignKey, key, principalType, otherEntityType, entityType);
+ GetString("ForeignKeyWrongType", nameof(foreignKey), nameof(key), nameof(principalType), nameof(entityType), nameof(otherEntityType)),
+ foreignKey, key, principalType, entityType, otherEntityType);
///
- /// The index {index} is defined on the entity type '{otherEntityType}', not '{entityType}'.
+ /// The index {index} cannot be removed from the entity type '{entityType}' because it is defined on the entity type '{otherEntityType}'.
///
- public static string IndexWrongType([CanBeNull] object index, [CanBeNull] object otherEntityType, [CanBeNull] object entityType)
+ public static string IndexWrongType([CanBeNull] object index, [CanBeNull] object entityType, [CanBeNull] object otherEntityType)
=> string.Format(
- GetString("IndexWrongType", nameof(index), nameof(otherEntityType), nameof(entityType)),
- index, otherEntityType, entityType);
+ GetString("IndexWrongType", nameof(index), nameof(entityType), nameof(otherEntityType)),
+ index, entityType, otherEntityType);
///
- /// The key {key} cis defined on the entity type '{otherEntityType}', not '{entityType}'.
+ /// The key {key} cannot be removed from the entity type '{entityType}' because it is defined on the entity type '{otherEntityType}'.
///
- public static string KeyWrongType([CanBeNull] object key, [CanBeNull] object otherEntityType, [CanBeNull] object entityType)
+ public static string KeyWrongType([CanBeNull] object key, [CanBeNull] object entityType, [CanBeNull] object otherEntityType)
=> string.Format(
- GetString("KeyWrongType", nameof(key), nameof(otherEntityType), nameof(entityType)),
- key, otherEntityType, entityType);
+ GetString("KeyWrongType", nameof(key), nameof(entityType), nameof(otherEntityType)),
+ key, entityType, otherEntityType);
///
- /// The specified property '{property}' is declared on the entity type '{otherEntityType}', not '{entityType}'.
+ /// The property '{property}' cannot be removed from the entity type '{entityType}' because it is declared on the entity type '{otherEntityType}'.
///
- public static string PropertyWrongType([CanBeNull] object property, [CanBeNull] object otherEntityType, [CanBeNull] object entityType)
+ public static string PropertyWrongType([CanBeNull] object property, [CanBeNull] object entityType, [CanBeNull] object otherEntityType)
=> string.Format(
- GetString("PropertyWrongType", nameof(property), nameof(otherEntityType), nameof(entityType)),
- property, otherEntityType, entityType);
+ GetString("PropertyWrongType", nameof(property), nameof(entityType), nameof(otherEntityType)),
+ property, entityType, otherEntityType);
///
/// There is no navigation on entity type '{entityType}' associated with the foreign key {foreignKey}.
@@ -2236,6 +2236,70 @@ public static string ClientProjectionCapturingConstantInTree([CanBeNull] object
GetString("ClientProjectionCapturingConstantInTree", nameof(constantType)),
constantType);
+ ///
+ /// Cannot remove foreign key {foreigKey} from entity type '{entityType}' because it is referenced by a skip navigation '{navigation}' on entity type '{navigationEntityType}'. All referencing skip navigation must be removed before the referenced foreign key can be removed.
+ ///
+ public static string ForeignKeyInUseSkipNavigation([CanBeNull] object foreigKey, [CanBeNull] object entityType, [CanBeNull] object navigation, [CanBeNull] object navigationEntityType)
+ => string.Format(
+ GetString("ForeignKeyInUseSkipNavigation", nameof(foreigKey), nameof(entityType), nameof(navigation), nameof(navigationEntityType)),
+ foreigKey, entityType, navigation, navigationEntityType);
+
+ ///
+ /// The skip navigation property '{navigation}' cannot be added to entity type '{entityType}' because it is expected to be on the dependent entity type '{dependentEntityType}' of the foreign key {foreigKey}.
+ ///
+ public static string SkipNavigationWrongDependentType([CanBeNull] object navigation, [CanBeNull] object entityType, [CanBeNull] object dependentEntityType, [CanBeNull] object foreigKey)
+ => string.Format(
+ GetString("SkipNavigationWrongDependentType", nameof(navigation), nameof(entityType), nameof(dependentEntityType), nameof(foreigKey)),
+ navigation, entityType, dependentEntityType, foreigKey);
+
+ ///
+ /// The skip navigation '{inverse}' declared on the entity type '{inverseEntityType}' cannot be set as the inverse of '{navigation}' that targets '{targetEntityType}'. The inverse should be declared on the target entity type.
+ ///
+ public static string SkipNavigationWrongInverse([CanBeNull] object inverse, [CanBeNull] object inverseEntityType, [CanBeNull] object navigation, [CanBeNull] object targetEntityType)
+ => string.Format(
+ GetString("SkipNavigationWrongInverse", nameof(inverse), nameof(inverseEntityType), nameof(navigation), nameof(targetEntityType)),
+ inverse, inverseEntityType, navigation, targetEntityType);
+
+ ///
+ /// The skip navigation property '{navigation}' cannot be added to entity type '{entityType}' because it is expected to be on the principal entity type '{principalEntityType}' of the foreign key {foreigKey}.
+ ///
+ public static string SkipNavigationWrongPrincipalType([CanBeNull] object navigation, [CanBeNull] object entityType, [CanBeNull] object principalEntityType, [CanBeNull] object foreigKey)
+ => string.Format(
+ GetString("SkipNavigationWrongPrincipalType", nameof(navigation), nameof(entityType), nameof(principalEntityType), nameof(foreigKey)),
+ navigation, entityType, principalEntityType, foreigKey);
+
+ ///
+ /// The skip navigation property '{navigation}' cannot be removed from the entity type '{entityType}' because it is defined on the entity type '{otherEntityType}'.
+ ///
+ public static string SkipNavigationWrongType([CanBeNull] object navigation, [CanBeNull] object entityType, [CanBeNull] object otherEntityType)
+ => string.Format(
+ GetString("SkipNavigationWrongType", nameof(navigation), nameof(entityType), nameof(otherEntityType)),
+ navigation, entityType, otherEntityType);
+
+ ///
+ /// The skip navigation '{inverse}' using the association entity type '{inverseAssociationType}' cannot be set as the inverse of '{navigation}' that uses the association entity type '{associationType}'. The inverse should use the same association entity type.
+ ///
+ public static string SkipInverseMismatchedAssociationType([CanBeNull] object inverse, [CanBeNull] object inverseAssociationType, [CanBeNull] object navigation, [CanBeNull] object associationType)
+ => string.Format(
+ GetString("SkipInverseMismatchedAssociationType", nameof(inverse), nameof(inverseAssociationType), nameof(navigation), nameof(associationType)),
+ inverse, inverseAssociationType, navigation, associationType);
+
+ ///
+ /// The skip navigation '{navigation}' on entity type '{entityType}' doesn't have an inverse configured. Every skip navigation should have an inverse skip navigation.
+ ///
+ public static string SkipNavigationNoInverse([CanBeNull] object navigation, [CanBeNull] object entityType)
+ => string.Format(
+ GetString("SkipNavigationNoInverse", nameof(navigation), nameof(entityType)),
+ navigation, entityType);
+
+ ///
+ /// The skip navigation '{navigation}' on entity type '{entityType}' is not a collection. Only collection skip navigation properties are currently supported.
+ ///
+ public static string SkipNavigationNonCollection([CanBeNull] object navigation, [CanBeNull] object entityType)
+ => string.Format(
+ GetString("SkipNavigationNonCollection", nameof(navigation), nameof(entityType)),
+ navigation, entityType);
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx
index 8192768d23d..d73698fd7fb 100644
--- a/src/EFCore/Properties/CoreStrings.resx
+++ b/src/EFCore/Properties/CoreStrings.resx
@@ -443,7 +443,7 @@
The property '{property}' cannot be removed from entity type '{entityType}' because it is being used in the key {key}. All containing keys must be removed or redefined before the property can be removed.
- Cannot remove key {key} from entity type '{entityType}' because it is referenced by a foreign key in entity type '{dependentType}'. All foreign keys must be removed or redefined before the referenced key can be removed.
+ Cannot remove key {key} from entity type '{entityType}' because it is referenced by a foreign key {foreignKey} in entity type '{dependentType}'. All foreign keys must be removed or redefined before the referenced key can be removed.The service property '{property}' of type '{serviceType}' cannot be added to the entity type '{entityType}' because service property '{duplicateName}' of the same type already exists on entity type '{duplicateEntityType}'.
@@ -1149,16 +1149,16 @@
The entity type '{entityType}' is part of a hierarchy, but does not have a discriminator value configured.
- The foreign key {foreignKey} targeting the key {key} on '{principalType}' is defined on the entity type '{otherEntityType}', not '{entityType}'.
+ The foreign key {foreignKey} targeting the key {key} on '{principalType}' cannot be removed from the entity type '{entityType}' because it is defined on the entity type '{otherEntityType}'.
- The index {index} is defined on the entity type '{otherEntityType}', not '{entityType}'.
+ The index {index} cannot be removed from the entity type '{entityType}' because it is defined on the entity type '{otherEntityType}'.
- The key {key} cis defined on the entity type '{otherEntityType}', not '{entityType}'.
+ The key {key} cannot be removed from the entity type '{entityType}' because it is defined on the entity type '{otherEntityType}'.
- The specified property '{property}' is declared on the entity type '{otherEntityType}', not '{entityType}'.
+ The property '{property}' cannot be removed from the entity type '{entityType}' because it is declared on the entity type '{otherEntityType}'.There is no navigation on entity type '{entityType}' associated with the foreign key {foreignKey}.
@@ -1218,4 +1218,28 @@
Client projection contains reference to constant expression of '{constantType}'. This could potentially cause memory leak. Consider assigning this constant to local variable and using the variable in the query instead. See https://go.microsoft.com/fwlink/?linkid=2103067 for more information.
+
+ Cannot remove foreign key {foreigKey} from entity type '{entityType}' because it is referenced by a skip navigation '{navigation}' on entity type '{navigationEntityType}'. All referencing skip navigation must be removed before the referenced foreign key can be removed.
+
+
+ The skip navigation property '{navigation}' cannot be added to entity type '{entityType}' because it is expected to be on the dependent entity type '{dependentEntityType}' of the foreign key {foreigKey}.
+
+
+ The skip navigation '{inverse}' declared on the entity type '{inverseEntityType}' cannot be set as the inverse of '{navigation}' that targets '{targetEntityType}'. The inverse should be declared on the target entity type.
+
+
+ The skip navigation property '{navigation}' cannot be added to entity type '{entityType}' because it is expected to be on the principal entity type '{principalEntityType}' of the foreign key {foreigKey}.
+
+
+ The skip navigation property '{navigation}' cannot be removed from the entity type '{entityType}' because it is defined on the entity type '{otherEntityType}'.
+
+
+ The skip navigation '{inverse}' using the association entity type '{inverseAssociationType}' cannot be set as the inverse of '{navigation}' that uses the association entity type '{associationType}'. The inverse should use the same association entity type.
+
+
+ The skip navigation '{navigation}' on entity type '{entityType}' doesn't have an inverse configured. Every skip navigation should have an inverse skip navigation.
+
+
+ The skip navigation '{navigation}' on entity type '{entityType}' is not a collection. Only collection skip navigation properties are currently supported.
+
\ No newline at end of file
diff --git a/test/EFCore.Cosmos.Tests/Infrastructure/CosmosModelValidatorTest.cs b/test/EFCore.Cosmos.Tests/Infrastructure/CosmosModelValidatorTest.cs
index adb97b9df6f..268d40d4335 100644
--- a/test/EFCore.Cosmos.Tests/Infrastructure/CosmosModelValidatorTest.cs
+++ b/test/EFCore.Cosmos.Tests/Infrastructure/CosmosModelValidatorTest.cs
@@ -128,27 +128,5 @@ public virtual void Detects_duplicate_discriminator_values()
}
protected override TestHelpers TestHelpers => CosmosTestHelpers.Instance;
-
- private class Customer
- {
- public int Id { get; set; }
- public string Name { get; set; }
- public string PartitionId { get; set; }
- public ICollection Orders { get; set; }
- }
-
- private class Order
- {
- public int Id { get; set; }
- public string PartitionId { get; set; }
- public Customer Customer { get; set; }
- public OrderDetails OrderDetails { get; set; }
- }
-
- [Owned]
- private class OrderDetails
- {
- public string ShippingAddress { get; set; }
- }
}
}
diff --git a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs
index 9ab4bba86da..6e0f7dea40a 100644
--- a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs
+++ b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs
@@ -723,7 +723,7 @@ public virtual void Passes_for_compatible_duplicate_foreignKey_names_within_hier
{
et.Property(c => c.Breed).HasColumnName("Breed");
fk2 = et
- .HasOne(a => (Customer)a.FavoritePerson)
+ .HasOne(a => (Employee)a.FavoritePerson)
.WithMany()
.HasForeignKey(
c => new { c.Name, c.Breed })
@@ -770,7 +770,7 @@ public virtual void Passes_for_compatible_duplicate_foreignKey_names_within_hier
{
et.Property(c => c.Breed).HasColumnName("Breed");
fk2 = et
- .HasOne()
+ .HasOne()
.WithMany()
.HasForeignKey(
c => new { c.Name, c.Breed })
@@ -1081,7 +1081,7 @@ protected class Person
public string FavoriteBreed { get; set; }
}
- protected class Customer : Person
+ protected class Employee : Person
{
}
diff --git a/test/EFCore.Tests/ApiConsistencyTest.cs b/test/EFCore.Tests/ApiConsistencyTest.cs
index 6dd026bc097..6967031db9d 100644
--- a/test/EFCore.Tests/ApiConsistencyTest.cs
+++ b/test/EFCore.Tests/ApiConsistencyTest.cs
@@ -74,6 +74,7 @@ public class ApiConsistencyTest : ApiConsistencyTestBase
{ typeof(IIndex), (typeof(IMutableIndex), typeof(IConventionIndex)) },
{ typeof(IProperty), (typeof(IMutableProperty), typeof(IConventionProperty)) },
{ typeof(INavigation), (typeof(IMutableNavigation), typeof(IConventionNavigation)) },
+ { typeof(ISkipNavigation), (typeof(IMutableSkipNavigation), typeof(IConventionSkipNavigation)) },
{ typeof(IServiceProperty), (typeof(IMutableServiceProperty), typeof(IConventionServiceProperty)) },
{ typeof(IPropertyBase), (typeof(IMutablePropertyBase), typeof(IConventionPropertyBase)) },
{ typeof(ModelExtensions), (typeof(MutableModelExtensions), typeof(ConventionModelExtensions)) },
diff --git a/test/EFCore.Tests/ApiConsistencyTestBase.cs b/test/EFCore.Tests/ApiConsistencyTestBase.cs
index 5907fe60943..b44d0517c67 100644
--- a/test/EFCore.Tests/ApiConsistencyTestBase.cs
+++ b/test/EFCore.Tests/ApiConsistencyTestBase.cs
@@ -145,8 +145,8 @@ private string MatchMutable((MethodInfo Readonly, MethodInfo Mutable) methodTupl
{
if (mutableMethod.ReturnType != expectedReturnTypes.Mutable)
{
- return
- $"{mutableMethod.DeclaringType.Name}.{mutableMethod.Name} expected to have {expectedReturnTypes.Mutable} return type";
+ return $"{mutableMethod.DeclaringType.Name}.{mutableMethod.Name}"
+ + $" expected to have {expectedReturnTypes.Mutable} return type";
}
}
else
@@ -157,8 +157,8 @@ private string MatchMutable((MethodInfo Readonly, MethodInfo Mutable) methodTupl
{
if (mutableMethod.ReturnType.TryGetSequenceType() != expectedReturnTypes.Mutable)
{
- return
- $"{mutableMethod.DeclaringType.Name}.{mutableMethod.Name} expected to have a return type that derives from IEnumerable<{expectedReturnTypes.Mutable}>.";
+ return $"{mutableMethod.DeclaringType.Name}.{mutableMethod.Name}"
+ + $" expected to have a return type that derives from IEnumerable<{expectedReturnTypes.Mutable}>.";
}
}
}
diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
index 6d61910a606..3a29b6135e7 100644
--- a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
+++ b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
@@ -478,6 +478,56 @@ public virtual void Detects_generic_leaf_type()
VerifyError(CoreStrings.AbstractLeafEntityType(entityGeneric.DisplayName()), model);
}
+ [ConditionalFact]
+ public virtual void Passes_on_valid_many_to_many_navigations()
+ {
+ var modelBuilder = CreateConventionalModelBuilder();
+
+ var model = modelBuilder.Model;
+ var orderProductEntity = model.AddEntityType(typeof(OrderProduct));
+ var orderEntity = model.FindEntityType(typeof(Order));
+ var productEntity = model.FindEntityType(typeof(Product));
+ var orderProductForeignKey = orderProductEntity
+ .GetForeignKeys().Single(fk => fk.PrincipalEntityType == orderEntity);
+ var productOrderForeignKey = orderProductEntity
+ .GetForeignKeys().Single(fk => fk.PrincipalEntityType == productEntity);
+ orderProductEntity.SetPrimaryKey(new[] { orderProductForeignKey.Properties.Single(), productOrderForeignKey.Properties.Single() });
+
+ var productsNavigation = orderEntity.AddSkipNavigation(
+ nameof(Order.Products), null, productEntity, orderProductForeignKey, true, true);
+
+ var ordersNavigation = productEntity.AddSkipNavigation(
+ nameof(Product.Orders), null, orderEntity, productOrderForeignKey, true, true);
+
+ productsNavigation.SetInverse(ordersNavigation);
+ ordersNavigation.SetInverse(productsNavigation);
+
+ Validate(model);
+ }
+
+ [ConditionalFact]
+ public virtual void Detects_missing_inverse_skip_navigations()
+ {
+ var modelBuilder = CreateConventionalModelBuilder();
+
+ var model = modelBuilder.Model;
+ var orderProductEntity = model.AddEntityType(typeof(OrderProduct));
+ var orderEntity = model.FindEntityType(typeof(Order));
+ var productEntity = model.FindEntityType(typeof(Product));
+ var orderProductForeignKey = orderProductEntity
+ .GetForeignKeys().Single(fk => fk.PrincipalEntityType == orderEntity);
+ var productOrderForeignKey = orderProductEntity
+ .GetForeignKeys().Single(fk => fk.PrincipalEntityType == productEntity);
+ orderProductEntity.SetPrimaryKey(new[] { orderProductForeignKey.Properties.Single(), productOrderForeignKey.Properties.Single() });
+
+ var productsNavigation = orderEntity.AddSkipNavigation(
+ nameof(Order.Products), null, productEntity, orderProductForeignKey, true, true);
+
+ VerifyError(
+ CoreStrings.SkipNavigationNoInverse(nameof(Order.Products), nameof(Order)),
+ model);
+ }
+
[ConditionalFact]
public virtual void Passes_on_valid_owned_entity_types()
{
diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTestBase.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTestBase.cs
index 640d6f3a144..8122637d51e 100644
--- a/test/EFCore.Tests/Infrastructure/ModelValidatorTestBase.cs
+++ b/test/EFCore.Tests/Infrastructure/ModelValidatorTestBase.cs
@@ -6,6 +6,7 @@
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;
using System.Linq;
+using System.Reflection;
using Microsoft.EntityFrameworkCore.Diagnostics.Internal;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
@@ -192,6 +193,55 @@ public int this[int index]
}
}
+ protected class Customer
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public string PartitionId { get; set; }
+ public ICollection Orders { get; set; }
+ }
+
+ protected class Order
+ {
+ public static readonly PropertyInfo IdProperty = typeof(Order).GetProperty(nameof(Id));
+
+ public int Id { get; set; }
+ public string PartitionId { get; set; }
+ public Customer Customer { get; set; }
+
+ public OrderDetails OrderDetails { get; set; }
+
+ [NotMapped]
+ public virtual ICollection Products { get; set; }
+ }
+
+ [Owned]
+ protected class OrderDetails
+ {
+ public string ShippingAddress { get; set; }
+ }
+
+ protected class OrderProduct
+ {
+ public static readonly PropertyInfo OrderIdProperty = typeof(OrderProduct).GetProperty(nameof(OrderId));
+ public static readonly PropertyInfo ProductIdProperty = typeof(OrderProduct).GetProperty(nameof(ProductId));
+
+ public int OrderId { get; set; }
+ public int ProductId { get; set; }
+ public virtual Order Order { get; set; }
+ public virtual Product Product { get; set; }
+ }
+
+ protected class Product
+ {
+ public static readonly PropertyInfo IdProperty = typeof(Product).GetProperty(nameof(Id));
+
+ public int Id { get; set; }
+
+ [NotMapped]
+ public virtual ICollection Orders { get; set; }
+ }
+
protected ModelValidatorTestBase()
=> LoggerFactory = new ListLoggerFactory(l => l == DbLoggerCategory.Model.Validation.Name || l == DbLoggerCategory.Model.Name);
diff --git a/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs b/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs
index 6e40eb0d0b8..9808d62787b 100644
--- a/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs
+++ b/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs
@@ -941,8 +941,17 @@ public void OnForeignKeyRemoved_calls_conventions_in_order(bool useScope)
var scope = useScope ? builder.Metadata.ConventionDispatcher.DelayConventions() : null;
- Assert.NotNull(
- entityBuilder.Metadata.RemoveForeignKey(foreignKey.Properties, foreignKey.PrincipalKey, foreignKey.PrincipalEntityType));
+ var result = entityBuilder.Metadata.RemoveForeignKey(
+ foreignKey.Properties, foreignKey.PrincipalKey, foreignKey.PrincipalEntityType);
+
+ if (useScope)
+ {
+ Assert.Same(foreignKey, result);
+ }
+ else
+ {
+ Assert.Null(result);
+ }
if (useScope)
{
@@ -1681,6 +1690,111 @@ public void ProcessNavigationAdded(
}
}
+ [InlineData(false, false)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(true, true)]
+ [ConditionalTheory]
+ public void OnNavigationAnnotationChanged_calls_conventions_in_order(bool useBuilder, bool useScope)
+ {
+ var conventions = new ConventionSet();
+
+ var convention1 = new NavigationAnnotationChangedConvention(terminate: false);
+ var convention2 = new NavigationAnnotationChangedConvention(terminate: true);
+ var convention3 = new NavigationAnnotationChangedConvention(terminate: false);
+ conventions.NavigationAnnotationChangedConventions.Add(convention1);
+ conventions.NavigationAnnotationChangedConventions.Add(convention2);
+ conventions.NavigationAnnotationChangedConventions.Add(convention3);
+
+ var builder = new InternalModelBuilder(new Model(conventions));
+ var principalEntityBuilder = builder.Entity(typeof(Order), ConfigurationSource.Convention);
+ var dependentEntityBuilder = builder.Entity(typeof(OrderDetails), ConfigurationSource.Convention);
+ var navigation = dependentEntityBuilder.HasRelationship(
+ principalEntityBuilder.Metadata, OrderDetails.OrderProperty, ConfigurationSource.Convention)
+ .Metadata.DependentToPrincipal;
+
+ var scope = useScope ? builder.Metadata.ConventionDispatcher.DelayConventions() : null;
+
+ if (useBuilder)
+ {
+ Assert.NotNull(navigation.Builder.HasAnnotation("foo", "bar", ConfigurationSource.Convention));
+ }
+ else
+ {
+ navigation["foo"] = "bar";
+ }
+
+ if (useScope)
+ {
+ Assert.Empty(convention1.Calls);
+ Assert.Empty(convention2.Calls);
+ scope.Dispose();
+ }
+
+ Assert.Equal(new[] { "bar" }, convention1.Calls);
+ Assert.Equal(new[] { "bar" }, convention2.Calls);
+ Assert.Empty(convention3.Calls);
+
+ if (useBuilder)
+ {
+ Assert.NotNull(navigation.Builder.HasAnnotation("foo", "bar", ConfigurationSource.Convention));
+ }
+ else
+ {
+ navigation["foo"] = "bar";
+ }
+
+ Assert.Equal(new[] { "bar" }, convention1.Calls);
+ Assert.Equal(new[] { "bar" }, convention2.Calls);
+ Assert.Empty(convention3.Calls);
+
+ if (useBuilder)
+ {
+ Assert.NotNull(navigation.Builder.HasAnnotation("foo", null, ConfigurationSource.Convention));
+ }
+ else
+ {
+ navigation.RemoveAnnotation("foo");
+ }
+
+ Assert.Equal(new[] { "bar", null }, convention1.Calls);
+ Assert.Equal(new[] { "bar", null }, convention2.Calls);
+ Assert.Empty(convention3.Calls);
+
+ navigation[CoreAnnotationNames.EagerLoaded] = true;
+
+ Assert.Equal(new[] { "bar", null }, convention1.Calls);
+ }
+
+ private class NavigationAnnotationChangedConvention : INavigationAnnotationChangedConvention
+ {
+ private readonly bool _terminate;
+ public readonly List