Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework 5 does not clear navigation property

I've got this strange issue with Entity Framework 5 where I have a navigation property in one of my entities that I would like to set to null. But for some reason the property only gets cleared the second time I call this property:

using (var db = new Entities())
{
    var vehicle = db.Vehicles.Single(v => v.Id == vehicleId);

    // After this call, ParkingBay is still set.
    vehicle.ParkingBay = null;

    // Only after this call, ParkingBay becomes null.
    vehicle.ParkingBay = null;

    db.SaveChanges();
}

The Vehicle class that Entity Framework generates looks like this:

public partial class Vehicle
{
    public int Id { get; set; }
    public System.DateTime CreatedOn { get; set; }
    public int CreatedBy { get; set; }
    public virtual ParkingBay ParkingBay { get; set; }
}

This code isn't very exiting, and at runtime Entity Framework generates proxy classes for both Vehicle and ParkingBay, but I can't understand what happens inside that ParkingBay property that fails to clear the property on the first call.

Between Vehicle and ParkingBay is a normal Foreign Key relationship in SQL Server. Nothing special here.

UPDATE

The ParkingBay looks like this:

public partial class ParkingBay
{
    public ParkingBay()
    {
        this.Vehicles = new HashSet<Vehicle>();
    }

    public int Id { get; set; }
    public System.DateTime CreatedOn { get; set; }
    public int CreatedBy { get; set; }

    public virtual ICollection<Vehicle> Vehicles { get; set; }
}

What's going on here? Can someone enlighten me what I'm doing wrong?

like image 870
Steven Avatar asked Mar 21 '14 11:03

Steven


2 Answers

After the query statement (db.Vehicles.Single ...) the property is null, because you don't load it. Assigning another value to it doesn't trigger a lazy load, so nothing changes here.

Only when the property is actually loaded an assignment (any assignment, also replacing it by another object) will have an effect. If the property isn't loaded, the change tracker has nothing to track.

The property can get loaded by including it in the query

db.Vehicles.Include(v => v.ParkingBay)...

or by addressing it later in the code, e.g.

var pb = vehicle.ParkingBay; // triggers lazy loading.

or by inspecting it in the debugger (watch or quickview), which also trigger lazy loading.

Include is the recommended approach if you intend to apply any changes to navigation properties themselves.

As commented below, a better performing way to clear a reference navigation property is to expose the primitive foreign key value in the model and set it to null. In your case, this would be something like int? ParkingBayId. This pattern is know as foreign key associations, as opposed to independent associations, when only the reference property is present.

like image 168
Gert Arnold Avatar answered Sep 22 '22 18:09

Gert Arnold


You actually need to load Property before setting it to null. I agree with Gert Arnold's answer, just wanted to add more alternatives to do it:

db.Entry(vehicle).Reference(c => c.ParkingBay).Load();
vehicle.ParkingBay = null;

OR

db.Entry(vehicle).Reference(v => v.ParkingBay).CurrentValue = null;

MSDN Relationships and Navigation Properties

like image 44
Oleksii Aza Avatar answered Sep 21 '22 18:09

Oleksii Aza