Skip to content

Commit

Permalink
Allow to override a navigation property discovered by convention with…
Browse files Browse the repository at this point in the history
… a property on the proncipal side.

Fixes #11199
  • Loading branch information
AndriySvyryd committed Mar 20, 2018
1 parent 3a5b2d7 commit 418cc45
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 41 deletions.
1 change: 1 addition & 0 deletions src/EFCore.Specification.Tests/CustomConvertersTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Xunit;

// ReSharper disable InconsistentNaming
namespace Microsoft.EntityFrameworkCore
{
public abstract class CustomConvertersTestBase<TFixture> : BuiltInDataTypesTestBase<TFixture>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,7 @@ private IReadOnlyList<RelationshipCandidate> FindRelationshipCandidates(Internal
var navigationPropertyInfo = candidateTuple.Key;
var targetClrType = candidateTuple.Value;

if (entityTypeBuilder.IsIgnored(navigationPropertyInfo.Name, ConfigurationSource.Convention)
|| (entityTypeBuilder.Metadata.IsQueryType
&& navigationPropertyInfo.PropertyType.TryGetSequenceType() != null))
if (!IsCandidateNavigationProperty(entityTypeBuilder, navigationPropertyInfo.Name, navigationPropertyInfo))
{
continue;
}
Expand Down Expand Up @@ -179,14 +177,15 @@ private IReadOnlyList<RelationshipCandidate> FindRelationshipCandidates(Internal

if (inverseTargetType != entityType.ClrType
|| navigationPropertyInfo.IsSameAs(inversePropertyInfo)
|| candidateTargetEntityTypeBuilder.IsIgnored(inversePropertyInfo.Name, ConfigurationSource.Convention)
|| entityType.IsQueryType
|| (ownership != null
&& (ownership.PrincipalEntityType != candidateTargetEntityType
|| ownership.PrincipalToDependent.Name != inversePropertyInfo.Name))
|| (entityType.HasDefiningNavigation()
&& (entityType.DefiningEntityType != candidateTargetEntityType
|| entityType.DefiningNavigationName != inversePropertyInfo.Name)))
|| entityType.DefiningNavigationName != inversePropertyInfo.Name))
|| !IsCandidateNavigationProperty(
candidateTargetEntityTypeBuilder, inversePropertyInfo.Name, inversePropertyInfo))
{
continue;
}
Expand Down Expand Up @@ -663,8 +662,10 @@ public virtual bool Apply(
string navigationName,
MemberInfo propertyInfo)
{
sourceEntityTypeBuilder = sourceEntityTypeBuilder.Metadata.Builder;
if (!IsCandidateNavigationProperty(sourceEntityTypeBuilder, navigationName, propertyInfo))
if ((targetEntityTypeBuilder.Metadata.Builder == null
&& sourceEntityTypeBuilder.ModelBuilder.IsIgnored(
targetEntityTypeBuilder.Metadata.Name, ConfigurationSource.Convention))
|| !IsCandidateNavigationProperty(sourceEntityTypeBuilder, navigationName, propertyInfo))
{
return true;
}
Expand Down Expand Up @@ -696,35 +697,13 @@ private bool Apply(EntityType entityType, MemberInfo navigationProperty)
[ContractAnnotation("propertyInfo:null => false")]
private static bool IsCandidateNavigationProperty(
InternalEntityTypeBuilder sourceEntityTypeBuilder, string navigationName, MemberInfo propertyInfo)
{
if (propertyInfo == null)
{
return false;
}

if (sourceEntityTypeBuilder == null
|| sourceEntityTypeBuilder.ModelBuilder.IsIgnored(sourceEntityTypeBuilder.Metadata.Name, ConfigurationSource.Convention))
{
return false;
}

if (sourceEntityTypeBuilder.IsIgnored(navigationName, ConfigurationSource.Convention))
{
return false;
}

if (sourceEntityTypeBuilder.Metadata.FindProperty(navigationName) != null)
{
return false;
}

if (sourceEntityTypeBuilder.Metadata.FindServiceProperty(navigationName) != null)
{
return false;
}

return true;
}
=> propertyInfo != null
&& sourceEntityTypeBuilder != null
&& !sourceEntityTypeBuilder.IsIgnored(navigationName, ConfigurationSource.Convention)
&& sourceEntityTypeBuilder.Metadata.FindProperty(navigationName) == null
&& sourceEntityTypeBuilder.Metadata.FindServiceProperty(navigationName) == null
&& (!sourceEntityTypeBuilder.Metadata.IsQueryType
|| (propertyInfo as PropertyInfo)?.PropertyType.TryGetSequenceType() == null);

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
Expand Down
12 changes: 8 additions & 4 deletions src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -398,15 +398,19 @@ private InternalPropertyBuilder Property(
if ((duplicateNavigation.IsDependentToPrincipal()
? foreignKey.GetDependentToPrincipalConfigurationSource()
: foreignKey.GetPrincipalToDependentConfigurationSource())
.Overrides(configurationSource)
&& configurationSource == ConfigurationSource.Explicit)
.Overrides(configurationSource))
{
throw new InvalidOperationException(CoreStrings.PropertyCalledOnNavigation(propertyName, Metadata.DisplayName()));
if (configurationSource == ConfigurationSource.Explicit)
{
throw new InvalidOperationException(CoreStrings.PropertyCalledOnNavigation(propertyName, Metadata.DisplayName()));
}

return null;
}

if (foreignKey.GetConfigurationSource() == ConfigurationSource.Convention)
{
RemoveForeignKey(foreignKey, ConfigurationSource.Convention);
foreignKey.DeclaringEntityType.Builder.RemoveForeignKey(foreignKey, ConfigurationSource.Convention);
}
else
{
Expand Down
3 changes: 2 additions & 1 deletion src/EFCore/Metadata/Internal/InternalRelationshipBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1444,7 +1444,8 @@ public virtual InternalRelationshipBuilder HasForeignKey(
return builder;
}

if (!CanSetForeignKey(properties, dependentEntityType, configurationSource, out var resetIsRequired, out var resetPrincipalKey))
if (!CanSetForeignKey(
properties, dependentEntityType, configurationSource, out var resetIsRequired, out var resetPrincipalKey))
{
return null;
}
Expand Down
16 changes: 16 additions & 0 deletions test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,22 @@ public virtual void Can_add_shadow_properties_when_they_have_been_ignored()
Assert.NotNull(modelBuilder.Model.FindEntityType(typeof(Customer)).FindProperty("Shadow"));
}

[Fact]
public virtual void Can_override_navigations_as_properties()
{
var modelBuilder = CreateModelBuilder();
var model = modelBuilder.Model;
modelBuilder.Entity<Customer>();

var customer = model.FindEntityType(typeof(Customer));
Assert.NotNull(customer.FindNavigation(nameof(Customer.Orders)));

modelBuilder.Entity<Customer>().Property(c => c.Orders);

Assert.Null(customer.FindNavigation(nameof(Customer.Orders)));
Assert.NotNull(customer.FindProperty(nameof(Customer.Orders)));
}

[Fact]
public virtual void Ignoring_a_navigation_property_removes_discovered_entity_types()
{
Expand Down

0 comments on commit 418cc45

Please sign in to comment.