简体   繁体   中英

Update entity on db with a property of list of entities with one-to-many relationship

I will make up some simpler entities to get a better understanding:

public class Room
{
    [Required]
    public int Id { get; set; }

    [Required]
    public int Name { get; set; }

    [Required]
    [Column("House_Id")]
    public int HouseId { get; set; }
    public virtual House House { get; set; }
}


public class House
{
    [Required]
    public int Id{ get; set; }

    public string Name { get; set; }

    public virtual ICollection<Room> Rooms { get; set; }
}

Now, lets say, that i have one House entity with 4 rooms related to it in database. I want to update the House and remove 2 of its rooms. I'm using Asp.Net MVC4 btw.

        var house = HouseRepository.Get(id);
        house.Name = model.Name; // Some string
        house.Name = model.Rooms; // ICollection<Room> from view with 2 rooms in it, cuz user deleted other two in view
        HouseRepository.SaveChanges();
        return house;

What happens, is that i get error saying : The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.

I need to find a solution how to delete the unrelated Rooms from db. When and where i should do it? I've tried something already, but now i get that error even I'm adding rooms.

Is there any simple way to tell EF that it should delete these rooms by itself?

EF can delete orphans automatically only if you use the identifying relationship. You need to define a composite key for the Room object, that consists of Id and HouseID .

modelbuilder.Entity<Room>()
            .HasKey(o => new { o.ID, o.HouseId });

modelbuilder.Entity<Room>()
            .Property(o => o.ID)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

With this configuration EF knows, that when you remove a room object from the Rooms collection, it's safe to remove the room for the DB as well.

Delete the Rooms you no longer want individually to explicitly remove them from the model - something equivalent to this:

foreach(Room room in house.Rooms.ToList())
{
    if(model.Rooms.FirstOrDefault(x=>x.Id == room.Id) == null)
         dbContext.Rooms.Remove(room); //assumes the room is attached to your context, you may need to do a get
}

Assuming your setup of 1 house that has 4 rooms currently associated to it, this should work:

var db = new TestContext();
var house = db.Houses.Include("Rooms").First();

// Grab rooms 2 & 3 for removal
var roomsToRemove = house.Rooms.OrderBy(r => r.Id).Skip(1).Take(2).ToList();
roomsToRemove.ForEach(r => db.Rooms.Remove(r));

db.SaveChanges();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM