Removing a navigation property does not update the database

I have 2 POCO classes (below) and you want to remove the link between my two posts. According to this, EF 5.0 should be able to handle deletion without loading the User class as follows:

context.Computers.Find("test").User = null; context.SaveChanges(); 

This does not work, but using the approved .net 4 method, it works:

 en = context.Computers.Find("test"); context.Entry(en).Reference(e => e.User).Load(); en.User = null; context.SaveChanges(); 

The reference to EF is EntityFramework.dll version 5.0.0.0. Did I miss something obvious here?

Here are the classes:

 public class Computer { public string Id { get; set; } public Nullable<int> UserId { get; set; } public virtual User User { get; set; } } public class User { public int Id { get; set; } public virtual ICollection<Computer> Computers { get; set; } } 

Edit: Here are the specific lines in the above related article that do not seem to be consistent with the functionality that I see:

To unlink, set the navigation property to null. If you are working with the Entity Framework based on .NET 4.0, you must bind the end associated with it before you set it to null. For instance:

 context.Entry(course).Reference(c => c.Department).Load(); course.Department = null; 

Starting with Entity Framework 5.0, based on .NET 4.5, you can set the null relationship without loading the bound end.

+6
source share
2 answers

You do not see that the connection is being deleted because your proxy server is not a change tracking proxy, but only a lazy download proxy. To make it a change tracking proxy, you need to set all properties to virtual. The following is an example that resets the navigation property without first loading it. Now the question is whether to use change tracking proxies - see this post as it contains an interesting discussion of this issue.

 public class Computer { public virtual string Id { get; set; } public virtual Nullable<int> UserId { get; set; } public virtual User User { get; set; } } public class User { public int Id { get; set; } public virtual ICollection<Computer> Computers { get; set; } } public class MyContext : DbContext { public DbSet<Computer> Computers { get; set; } public DbSet<User> Users { get; set; } } class Program { static void Main(string[] args) { using (var ctx = new MyContext()) { if (!ctx.Computers.Any()) { var user = ctx.Users.Add(new User()); ctx.Computers.Add(new Computer() { Id = "MyComputer", User = user }); ctx.SaveChanges(); } } using (var ctx = new MyContext()) { var computer = ctx.Computers.Single(); computer.User = null; ctx.SaveChanges(); } using (var ctx = new MyContext()) { var computer = ctx.Computers.Include("User").Single(); Console.WriteLine(computer.User == null); } } } 
+6
source

EDIT:

I just read the comments (I probably should have read them first, but I already wrote it like that, leave it alone) and we will see that you guys have come to pretty similar conclusions, I hope this still helps explain why. Also on the side of the note, I believe that you are better off using the id FK property for the null relation, if available, as that means you don’t have to actually load the remote object at all.


Hey, so I think this is happening:

Download computer object

  • at this point, the user's navigation property is null
  • The tracking graph has an initial state of this object equal to null

You set the User property to null

  • the tracking graph is still set to null, so it is not possible to tell the difference between your intentional zero and unloaded, but not equal to zero

You save changes

  • The tracker checks the initial schedule, sees that you have not yet loaded the user property and treat null as an unloaded initial state
  • No changes are saved.

If this is the way I see, there are two different ways to make EF detect this as a change (and therefore remove your relationship):

  • Forcing the user to load before setting the relation to null, you can do this either by accessing it, as with lazy loading enabled, or using the .Include syntax in your request.
  • Set the UserId property to null instead

The second is much simpler and should work, since the property of the user ID is not located on the remote object. EF will handle the null nav or null FK property as a change and perform the deletion.

+1
source

All Articles