sheamus
sheamus

Reputation: 3153

Entity Framework Core can't resolve inverse properties even with annotation

I have boiled this down to a pretty minimal use case:

public class ItemRental
{
    [Key]
    public Int32 ItemRentalId { get; set; }

    public Int32? OriginatingSalesOrderId { get; set; }

    [ForeignKey("OriginatingSalesOrderId")]
    public SalesOrder OriginatingSalesOrder { get; set; }

    public Int32? DepositCreditedOnSalesOrderId { get; set; }

    [ForeignKey("DepositCreditedOnSalesOrderId")]
    public SalesOrder DepositCreditedOnSalesOrder { get; set; }
}

public class SalesOrder
{

    [Key]
    public Int32 SalesOrderId { get; set; }

    [InverseProperty("OriginatingSalesOrder")]
    public ICollection<ItemRental> Rentals { get; set; }


    [InverseProperty("DepositCreditedOnSalesOrder")]
    public ICollection<ItemRental> Refunds { get; set; }
}

public class MyAppDatabase : DbContext
{
    public MyAppDatabase(DbContextOptions<MyAppDatabase> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(x => x.GetForeignKeys()))
        {
            relationship.DeleteBehavior = DeleteBehavior.Restrict;
        }
        base.OnModelCreating(modelBuilder);
    }

    public DbSet<ItemRental> ItemRentals { get; set; }
    public DbSet<SalesOrder> SalesOrders { get; set; }
}

Trying to run the migration will give:

System.InvalidOperationException: Unable to determine the relationship represented by navigation property 'ItemRental.OriginatingSalesOrder' of type 'SalesOrder'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.

This same relationship is perfectly fine with EF 6.x. I am sure I could solve this using the Fluent API, but I would rather understand how to make this work using annotations.

I found a similar question here: EntityFramework core model relationship issue while doing Add-Migration But it doesn't solve this problem.

EDIT: Sample solution here: https://drive.google.com/file/d/0BzgvtZfXt8MHd1RseVJubmd6TEU/view?usp=sharing

Upvotes: 0

Views: 5078

Answers (2)

Anatoly Isakov
Anatoly Isakov

Reputation: 1

Ivan is fully correct.

I used next simple way to avoid the issue (with this way you do not need to rename a column):

public class ItemRental
{
    [Column("ItemRentalId")] //new
    public Int32 Id { get; set; } //new

    public Int32? OriginatingSalesOrderId { get; set; }

    [ForeignKey("OriginatingSalesOrderId")]
    public SalesOrder OriginatingSalesOrder { get; set; }

    public Int32? DepositCreditedOnSalesOrderId { get; set; }

    [ForeignKey("DepositCreditedOnSalesOrderId")]
    public SalesOrder DepositCreditedOnSalesOrder { get; set; }
}

public class SalesOrder
{
    [Column("SalesOrderId")] //new
    public Int32 Id { get; set; } //new

    [InverseProperty("OriginatingSalesOrder")]
    public ICollection<ItemRental> Rentals { get; set; }

    [InverseProperty("DepositCreditedOnSalesOrder")]
    public ICollection<ItemRental> Refunds { get; set; }
}

public class MyAppDatabase : DbContext
{
    public MyAppDatabase(DbContextOptions<MyAppDatabase> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(x => x.GetForeignKeys()))
        {
            relationship.DeleteBehavior = DeleteBehavior.Restrict;
        }
        base.OnModelCreating(modelBuilder);
    }

    public DbSet<ItemRental> ItemRentals { get; set; }
    public DbSet<SalesOrder> SalesOrders { get; set; }
    }

Upvotes: 0

Ivan Stoev
Ivan Stoev

Reputation: 205589

There is nothing to understand here because your data annotations are perfectly valid.

The cause of the issue is more trivial - EF Core 2.0 (regression) bug tracked by #9180 Metadata: InverseProperty fails to resolve ambiguity while use KeyAttribute on PK open issue in EFC issue tracker, scheduled for the next 2.1 release.

Until then, the suggested workaround in the link is to use fluent API, but also it works if you remove Key attribute from SalesOrderId property in SalesOrder (since it luckily follows the one of the recognized PK conventions):

public class SalesOrder
{    
    public Int32 SalesOrderId { get; set; }    
    // ...
}

or if you apply the InverseProperty attribute on collection navigation properties:

public class ItemRental
{
    // ...
    [ForeignKey("OriginatingSalesOrderId")]
    [InverseProperty("Rentals")]
    public SalesOrder OriginatingSalesOrder { get; set; } 
    // ...    
    [ForeignKey("DepositCreditedOnSalesOrderId")]
    [InverseProperty("Refunds")]
    public SalesOrder DepositCreditedOnSalesOrder { get; set; }
}

Upvotes: 5

Related Questions