How to update FK to null when deleting optional related entity

c# cascading-deletes ef-code-first entity-framework

Question

I'm still learning how to use EF, therefore I'm having a little trouble deleting my objects. My two objects and related DbContext have the following appearance:

public class Context: DbContext
{
    public Context() : base(){}
    public DbSet<Person> Persons {get;set;}
    public DbSet<Vehicle> Vehicles {get;set;}
}

public class Person
{
   public int PersonID {get;set;}
   public string Name {get;set;}
}

public class Vehicle
{
   public int VehicleID {get;set;}

   public int? PersonID {get;set;}

   [ForeignKey("PersonID")]
   public virtual Person Person {get;set;}
} 

One individual can be connected to several automobiles, as mentioned above. There is no direct connection between the person and the car, but the foreign key relationship creates a connection between the car and its "parent" person.

I then generate a variety of vehicles in my code and connect them to objects representing zzz-15 zzz people (foreign key is nullable).

Regarding deleting Person objects, I have a question. I often delete the objects in the following manner:

private void DeletePerson()
{
    using (var context = new Context())
    {
        int personID = 4; //Determined through other code
        var person = context.Persons.Find(personID);
        context.Persons.Remove(person);
        context.SaveChanges();
    }
}

But the code above would throw a reference constraint exception, failing (due to the vehicle foreign key). But I would have anticipated that the foreign keys of all the cars connected to that one individual would just be set to null?

By explicitly loading all pertinent vehicles to the context, I was able to get the code to run as follows:

private void DeletePerson()
{
    using (var context = new Context())
    {
        //Determined through other code
        int personID = 4; 
        // Single vehicle associated with this person, there can be multiple vehicles
        int vehicleID = 6; 

        var person = context.Persons.Find(personID);
        // Seems to force loading of the vehicle, facilitating setting 
        // its "PersonID" property to null
        var vehicle = context.Vehicles.Find(vehicleID); 
        context.Persons.Remove(person);
        context.SaveChanges();
    }
}

However, the issue with the aforementioned code is that I must build an object called a List inside of my Person class that includes a reference (or ID) to all potential dependent items (vehicles is just one example here, there will be various other similar classes with similar relationships to Person).

Is the only method for doing this the construction of this List in the Person object? And is there a method to automate the addition of the dependents and the development of this list? In my Person class, I'd rather not have to explicitly maintain these relationships using a list object.

Thanks!

1
11
11/25/2015 10:36:23 AM

Accepted Answer

Set the Delete Rule in Entity Framework using CodeFirst Despite the fact that SQL Server enables it, EF is unable to implement a cascade rule to nullify the FK when the associated object is destroyed.

Therefore, you must add the relevant items to the context so that when you delete thePerson Updated with a null value are the connected automobiles.PersonId . There is no need for a list in this. You may perform theDbContext aware of the following connected entities:

ctx.Vehicles.Where(v => v.PersonId == personId).Load();

When you call delete after that, everything will go as planned.

Here is an exampleDbContext fluent API has been configured, and it performs as expected:

public class SampleDbContext: DbContext
{
    public SampleDbContext()
        : base("name=CascadeOnDelete")
    {

    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Vehicle>()
            .HasOptional(v => v.Person)
            .WithMany()
            .HasForeignKey(v => v.PersonId);
            //.WillCascadeOnDelete();
        base.OnModelCreating(modelBuilder);
    }

    public DbSet<Person> Persons {get;set;}
    public DbSet<Vehicle> Vehicles {get;set;}
}

public class Person
{
    public int PersonId {get;set;}
    public string Name {get;set;}
}

public class Vehicle
{
    public int VehicleId {get;set;}
    public string Model { get; set; }
    public int? PersonId { get; set; }
    public virtual Person Person {get;set;}
} 

Additionally, this console application behaves as expected:

class Program
{
    static void Main(string[] args)
    {
        using (var ctx = new SampleDbContext())
        {
            Console.WriteLine("Creating John McFlanagan and their 2 vehicles");
            var person = new Person {Name = "John McFlanagan"};
            var vehicle1 = new Vehicle { Person = person, Model = "Vauxhall Astra" };
            var vehicle2 = new Vehicle { Person = person, Model = "Ford Capri" };

            ctx.Vehicles.AddRange(new[] {vehicle1, vehicle2});
            ctx.SaveChanges();
        }

        using (var ctx = new SampleDbContext())
        {
            var person = ctx.Persons.First();
            // Loading related vehicles in the context
            ctx.Vehicles.Where(v => v.PersonId == person.PersonId).Load();
            Console.WriteLine("Deleting the person, and nullifying vehicles PersonId");
            ctx.Persons.Remove(person);
            ctx.SaveChanges();
        }

    }
}

It is possible to set the behavior in (EF7) EF Core.

Thanks for the feedback, @Dabblernl: http://blogs.msdn.com/b/adonet/archive/2015/10/15/ef7-beta-8-available.aspx#comments

Diego B Vega [MSFT] 17 Oct 2015 9:21 PM # @DabblerNL yes, the functionality is already implemented in current nightly builds. You will have to explicitly specify it in the model using .OnDelete(DeleteBehavior.SetNull).

The preceding link has expired. The description of this model attribute is available here: http://www.learnentityframeworkcore.com/conventions/one-to-many-relationship

10
5/23/2019 9:26:42 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow