Edgar.A
Edgar.A

Reputation: 1383

0-1 self referencing relationship in EF Core

Idea: I have a Key entity, which can be replaced (rotated) by other Key with a delay. I was thinking of implementing it as:

class Key {
    string Id;
    string ReplacesId;
    Key Replaces;
    string ReplacedById;
    Key ReplacedBy;
    DateTime Expires;
    // ...
}

So when I want to replace it, I'd create another key and then reference new and old keys to each other. And if anyone of them is deleted, the other one would be as well.

And that's how I implemented, with this Fluent mapping:

modelBuilder.Entity<Key>().HasOne(x => x.ReplacedBy)
            .WithOne(x => x.Replaces)
            .IsRequired(false)
            .OnDelete(DeleteBehavior.Cascade);

Replacement logic:

oldKey.ReplacedBy = newKey;
oldKey.ReplacedById = newKey.Id;
newKey.Replaces = oldKey;
newKey.ReplacesId = oldKey.Id;

_dbContext.Keys.Update(newKey);
_dbContext.Keys.Update(oldKey);

await _dbContext.SaveChangesAsync();

But it seems that ReplacedById ReplacesId or one of them are used like regular fields ant not FK, because when I try to delete one of them, they don't cascade.

Could I make this work, or should I just introduce Rotations/Replacements table?

Upvotes: 4

Views: 434

Answers (1)

TanvirArjel
TanvirArjel

Reputation: 32059

But it seems that ReplacedById ReplacesId or one of them are used like regular fields ant not FK, because when I try to delete one of them, they don't cascade.

This is because your Fluent API configuration is not done in correct way. It actually should be as follows:

modelBuilder.Entity<Key>().HasOne(k => k.Replaces)
                          .WithOne()
                          .HasForeignKey<Key>(k => k.ReplacesId)
                          .IsRequired(false);

modelBuilder.Entity<Key>().HasOne(k => k.ReplacedBy)
                          .WithOne()
                          .HasForeignKey<Key>(k => k.ReplacedById)
                          .IsRequired(false);

Now everything will generate and work as expected!

Note: It will generate onDelete: ReferentialAction.Restrict because you cannot cascade between them because it will cause multiple cascade paths.

Upvotes: 2

Related Questions