Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.0.0-rc2] Throw for ForeignKeyAttribute on skip navigation #22534

Merged
merged 1 commit into from
Sep 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/EFCore/Metadata/Conventions/ForeignKeyAttributeConvention.cs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ private static ForeignKeyAttribute GetForeignKeyAttribute(IConventionTypeBase en
&& Attribute.IsDefined(p, typeof(ForeignKeyAttribute), inherit: true))
?.GetCustomAttribute<ForeignKeyAttribute>(inherit: true);

private static ForeignKeyAttribute GetForeignKeyAttribute(IConventionNavigation navigation)
private static ForeignKeyAttribute GetForeignKeyAttribute(IConventionNavigationBase navigation)
=> GetAttribute<ForeignKeyAttribute>(navigation.GetIdentifyingMemberInfo());

private static InversePropertyAttribute GetInversePropertyAttribute(IConventionNavigation navigation)
Expand Down Expand Up @@ -451,6 +451,17 @@ var fkPropertyOnPrincipal
}
}
}

foreach (var declaredSkipNavigation in entityType.GetDeclaredSkipNavigations())
{
var fkAttribute = GetForeignKeyAttribute(declaredSkipNavigation);
if (fkAttribute != null
&& declaredSkipNavigation.ForeignKey?.GetPropertiesConfigurationSource() != ConfigurationSource.Explicit)
{
throw new InvalidOperationException(
CoreStrings.FkAttributeOnSkipNavigation(entityType.DisplayName(), declaredSkipNavigation.Name));
}
}
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/EFCore/Properties/CoreStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/EFCore/Properties/CoreStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,9 @@
<data name="FkAttributeOnPropertyNavigationMismatch" xml:space="preserve">
<value>The [ForeignKey] attributes on property '{property}' and navigation '{navigation}' in entity type '{entityType}' do not point at each other. The value of the [ForeignKey] attribute on the property should be navigation name, and the value of the [ForeignKey] attribute on the navigation should be the foreign key property name.</value>
</data>
<data name="FkAttributeOnSkipNavigation" xml:space="preserve">
<value>The [ForeignKey] attribute cannot be specified on the skip navigation '{entityType}'.'{navigation}'. Configure the foreign key properties in 'OnModelCreating' instead.</value>
</data>
<data name="ForeignKeyCountMismatch" xml:space="preserve">
<value>The number of properties specified for the foreign key {foreignKeyProperties} on entity type '{dependentType}' does not match the number of properties in the principal key {principalKey} on entity type '{principalType}'.</value>
</data>
Expand Down
50 changes: 50 additions & 0 deletions test/EFCore.Tests/ModelBuilding/ManyToManyTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
Expand Down Expand Up @@ -344,6 +345,55 @@ public virtual void Throws_for_many_to_many_with_only_one_navigation_configured(
.WithMany(d => d.ManyToManyPrincipals)).Message);
}

[ConditionalFact]
public virtual void Throws_for_ForeignKeyAttribute_on_navigation()
{
var modelBuilder = CreateModelBuilder();

modelBuilder.Entity<CategoryWithAttribute>();

Assert.Equal(
CoreStrings.FkAttributeOnSkipNavigation(
nameof(ProductWithAttribute), nameof(Product.Categories)),
Assert.Throws<InvalidOperationException>(
() => modelBuilder.FinalizeModel()).Message);
}

[ConditionalFact]
public virtual void Overrides_ForeignKeyAttribute()
{
var modelBuilder = CreateModelBuilder();

modelBuilder.Entity<CategoryWithAttribute>()
.HasMany(e => e.Products)
.WithMany(e => e.Categories)
.UsingEntity<Dictionary<string, object>>(
"ProductCategory",
e => e.HasOne<ProductWithAttribute>().WithMany().HasForeignKey("ProductKey"),
e => e.HasOne<CategoryWithAttribute>().WithMany().HasForeignKey("CategoryKey"));

var model = modelBuilder.FinalizeModel();

var category = model.FindEntityType(typeof(CategoryWithAttribute));
var productsNavigation = category.GetSkipNavigations().Single();
var categoryFk = productsNavigation.ForeignKey;
Assert.Equal("CategoryKey", categoryFk.Properties.Single().Name);
}

protected class ProductWithAttribute
{
public int Id { get; set; }

[ForeignKey("ProductId")]
public virtual ICollection<CategoryWithAttribute> Categories { get; set; }
}

protected class CategoryWithAttribute
{
public int Id { get; set; }
public virtual ICollection<ProductWithAttribute> Products { get; set; }
}

[ConditionalFact]
public virtual void Navigation_properties_can_set_access_mode()
{
Expand Down