Relationship not set to null
Asked Answered
A

1

5

I have a very simple EF operation that fails: break the relationship between two entities as shown in the code below:

public async Task RemoveCurrentTeacherOfGroup(int groupId)
{
    var group = await _dataContext.Groups.SingleAsync(g => g.Id == groupId);
    group.Teacher = null;
    await _dataContext.SaveChangesAsync();
}

The database was generated code-first. The entities are defined like this:

public class Teacher
{
    public int Id { get; set; }
    ..
    public virtual List<Group> Groups { get; set; }
}
public class Group
{
    public int Id { get; set; }
    ..
    public virtual Teacher Teacher { get; set; }
}

However, breaking the relationship doesn't work, Teacher keeps pointing to the same entity. When stepping with the debugger, I see that the Teacher property doesn't become null after .Teacher = null. I tried it with the synchronous alternative, which had the same effect:

public void RemoveCurrentTeacherOfGroup(int groupId)
{
    var group = _dataContext.Groups.Single(g => g.Id == groupId);
    group.Teacher = null;
    _dataContext.SaveChanges();
}
Aqua answered 22/1, 2015 at 16:22 Comment(0)
T
11

If Teacher is not loaded you can't break the relationship. Either include it (eager-load) on the query:

_dataContext.Groups.Include(g => g.Teacher).Single(g => g.Id == groupId);

Or if lazy loading is enabled, access the property for reading before setting it to null:

var teacher = group.Teacher;
group.Teacher = null;

You see that "Teacher is not null after setting it to null" because the debugger is accessing the property for reading (lazy-loading it) after you have set it to null.

The value was already null before you hit the group.Teacher = null line since you hadn't previously loaded it (you can't however debug this, since accessing the property for reading would cause EF to actually load it if lazy loading is enabled). If you see the property value with the debugger before setting it to null, it'll work as expected and break the relationship, since Teacher would be loaded

Translatable answered 22/1, 2015 at 16:31 Comment(9)
Thank you! Off topic: I think this is a shortcoming in EF. The proxy object could detect setting to null in its setter; the foreign key is known (it is used for lazy loading), so EF could remove it and set the the property to modified state.Aqua
@Aqua I guess it's for performance reasons: if you are going to just "set the related entity" there's no need to roundtrip to the database to see if "it was null because it wasn't loaded, or it was null because it was actually null". Everything that roundtrips to the database must be carefully done (and setting the related entity does not necessarily need it)Translatable
However, that information is already known: inside the proxy object the foreign key is stored. So EF already knows the relation is only null because of lazy-loading.Aqua
Yes, but that's only a very specific case, there are far too many possible scenarios in EF (6, at least) for that check to be consistent and performant (ever tried to figure out relationship state changes from the Metadata in EF6? :-) ). I guess EF7 will be more to-the-point and things like these could be implemented (since they are removing all the bloat that noone uses :-) )Translatable
This means you have both the Group and the Teacher loaded in the context. Which is fine, but another way to do it is to use a foreign key, then you only have to have the Group loaded https://mcmap.net/q/2029741/-entity-framework-update-foreign-key-to-null-without-query-and-without-id-of-the-related-entityAnnabellannabella
@Annabellannabella I agree, and I do it that way out of necessity (also for "relationship change tracking"), but in my head, this breaks the whole concept of ORM, by having to use "database-things" (a foreign key doesn't really make sense in an object->object relationship) in POCO classes. It's a tradeoff that you have to put up with for many things, but I hate that you have to.Translatable
That's why I linked to Julie Lerman's article on making do with absent foreign keys in that other question. Here's a direct link to Julie's article: msdn.microsoft.com/en-us/magazine/hh708747.aspxAnnabellannabella
@Annabellannabella yes... that does not work for other scenarios though (for example, it won't help you track changes automatically unless you get into the scary deep inners of ObjectContext ... which will also be deprecated on EF7), and also requires "direct" access to the dbcontext for the logic, or would make it way more complicated for a layered app where the model classes are not connected to the dbcontext directly. It is doable, but requires a lot of mangling: if I have to choose one of the two bad situations, I'd rather just add the foreign key, but definitely YMMV. I just don't like either :-)Translatable
RIP 2hours of my life. Thank you!Grampus

© 2022 - 2024 — McMap. All rights reserved.