question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Migration difference on Delete: No Foreign Key Property generating a Shadow Property vs Fully Defined Relationships

See original GitHub issue

Is the following a bug or per design? In case of a design constraint, it will be great to have an explanation and perhaps update the documentation on why there is a difference between FK set as Shadow Properties instead of declared in a CLR class when it comes to deletion.

Here are the articles that I followed No Foreign Key Property vs Fully Defined Relationships Shadow Properties Cascade Delete video uploaded on Aug 2015

The last link is a 1 year old video and I wonder if the scope of shadow properties has changed since then. In particular, at 6.30 min Seth asks the question if adding a shadow property and doing migrations would change anything and Rowan answers that it does nothing to the migrations pipeline and that it behaves exactly the same, as if this was defined on a CLR class. However, the code generated by the migration in .Net Core 1.1 is different when it comes to delete operations. Hence my question on whether this is per design or a bug.

The issue

Without Shadow Property using EF conventions (Fully Defined Relationships)

public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }

        public List<Post> Posts { get; set; }
    }
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }

        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }

add-migration init

This gives as a constraint in xxx.init.cs onDelete: ReferentialAction.Cascade;

I can then query, remove and save like this

var blog = db.Blogs.Find(id);
db.Remove(blog);
var count = db.SaveChanges();

or like that

var blog = db.Blogs.Find(id);
var blog = db.Blogs.Include(b => b.Posts)
.FirstOrDefault(b => b.BlogId == id);
db.Remove(blog);
var count = db.SaveChanges();

With Shadow Property (No Foreign Key Property)

public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }

        public List<Post> Posts { get; set; }
    }
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }

        public Blog Blog { get; set; }
    }

add-migration init

This gives a different constraint in xxx.init.cs onDelete: ReferentialAction.Restrict;

This will not be an issue if I query with an Include, remove a blog and save, but will throw an error if I do not use .Include, with an error The DELETE statement conflicted with the REFERENCE constraint “FK_Posts_Blogs_BlogId”.

Switching
onDelete: ReferentialAction.Restrict

to onDelete: ReferentialAction.Cascade

will make it work again. Alternatively, I would have had to delete all posts before deleting the blog.

Even though I can see some sort of coherence in the essence of a shadow property, I still do not understand why the delete constraint has been set to onDelete: ReferentialAction.Restrict.

Further technical details

EF Core version: .Net Core 1.1 Operating system: W10 Visual Studio version: VS 2017 RC

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:18 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
ajcvickerscommented, Nov 28, 2016

@Ponant I think maybe the confusion comes from understanding which are EF behaviors and which are database behaviors. For optional relationships, EF will always attempt to set null for any FK property in a tracked entity regardless of the cascading action. This is considered a normal part of fixup–previous versions of EF also worked this way and its not something we wanted to change. For required relationships, EF will only delete tracked dependents if cascade delete is configured. For both these cases EF does not suffer from the cycle issues that SQL Server does. So if all entities are loaded, then EF will have the correct behavior regardless of what the database is capable of.

When using Migrations to create a database, EF attempts to set the database to mimic EF behavior. This can allow an optimization in application code where dependent entities are not loaded but the effect is the same as if they were. Unfortunately, this doesn’t work well with SQL Server because of the cycles issue. Because of this we decided to not set null by default because it makes pretty much every model built for SQL Server fail and it is a less common behavior to want in the database. For deletes, we attempt to set it in the database, but if you have a cycle, then you must make an explicit decision to either forgo the database optimization or stop doing cascade deletes altogether.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Changing Foreign Keys and Navigations - EF Core
How to change relationships between entities by manipulating foreign keys and navigations.
Read more >
Entity Framework Core One-To-Many Relationships ...
EF Core will generate a shadow property for the foreign key named AuthorId , which will map to a nullable AuthorId foreign key...
Read more >
Why EF Core keeps adding shadow properties to migration ...
Specify ON DELETE NO ACTI ON or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. Is the issue in my entities...
Read more >
One-to-Many Relationships Conventions in Entity ...
EF Core will create a shadow property for the foreign key named GradeId in the conceptual model, which will be mapped to the...
Read more >
Cascade Delete in Entity Framework Core
When we delete the parent, the Context sets values of foreign key properties of the dependent entities to null on the entities it...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found