Edit
This seems to occur for any Entity property that references another entity in one direction. In other words, for the below example, the fact that Bar
overrides Equality appears to be irrelevant.
Suppose I have the following classes:
public class Foo
{
public int? Id { get; set; }
public virtual Bar { get; set; }
}
public class Bar : IEquatable<Bar>
{
public int Id { get; set; }
public override bool Equals(object obj)
{
var other = obj as Bar;
return Equals(other);
}
public bool Equals(Bar other)
{
if (object.Equals(other, null))
return false;
return this.Id == other.Id;
}
public static bool operator ==(Bar left, Bar right)
{
return object.Equals(left, right);
}
public static bool operator !=(Bar left, Bar right)
{
return !object.Equals(left, right);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
Note that here, "Bar" intentionally has "Id" equality, because it more or less represents a lookup table - so any two object references with the same Id should always be considered the same.
Here's the weird part, this all works fine when I set Foo.Bar
to another Bar
instance - everything updates as expected.
However, if foo
has an existing Bar
when it is retrieved from the DbContext
and I do:
foo.Bar = null
then the property doesn't actually change!
If I do:
var throwAway = foo.Bar;
foo.Bar = null;
Then the property will actually set and save as null.
Since the Foo.Bar
property is simply a virtual, auto-implemented property, I can only conclude that this has something to do with lazy-loading and Entity Framework proxies - but why this particular scenario causes a problem, I have no idea.
Why does Entity Framework behave this way, and how can I get it to actually set null
reliably?
Personally, I think Nathan's answer (lazy-loading inside the property setter) is the most robust. However, it mushrooms your domain classes (10 lines per property) and makes it less readable.
As another workaround, I compiled two methods into a extension method:
This allows you to supply a DbContext if you have one, in which case it will use the most efficient method and set the CurrentValue of the Entry Reference to null.
If no DBContext is supplied, it will lazy load first.
I am not happy with the official workaround:
because it involves too much contextual knowledge by the user of the POCO object. My fix is to trigger a load of the lazy property when setting the value to null so that we do not get a false positive comparison from EF:
and in my base DbEntity class:
Passing the property to the LazyPropertyIsNull function triggers the lazy load and the correct comparison occurs.
Please vote for this issue on the EF issues log:
As a workaround, the easiest way I've found to mitigate this issue is to have the setter call the getter before setting the backing field to null, e.g.
This way, the property works regardless of whether other code has actually loaded the property yet, at the cost of a potentially unneeded entity load.
You are right - this happens beacause you used lazy loading in EF (
virtual
property). You may removevirtual
(but this may be impossible for you). Other way you described in your question - call property, and set this to null.Also you could read another topic about this problem on SO.
If you want to avoid manipulating the
EntityEntry
, you can avoid the lazy load call to the database by including the FK property in your POCO (which you can make private if you don't want users to have access) and have the Nav property setter set that FK value to null if the settervalue
is null. Example:Test:
A way to make it work is using the property API:
The benefit is that this works without loading the
foo.Bar
by lazy loading and it also works for pure POCOs that don't support lazy loading or change tracking proxies (novirtual
properties). The downside is that you need acontext
instance available at the place where you want to set the relatedBar
tonull
.