【问题标题】:Update entity on db with a property of list of entities with one-to-many relationship使用具有一对多关系的实体列表的属性更新 db 上的实体
【发布时间】:2013-10-22 06:47:10
【问题描述】:

我会编一些更简单的实体来更好地理解:

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; }
}

现在,假设我在数据库中有一个 House 实体,其中有 4 个房间与之相关。我想更新房子并移除其中的 2 个房间。我正在使用 Asp.Net MVC4 顺便说一句。

        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;

发生的情况是,我收到错误消息:操作失败:无法更改关系,因为一个或多个外键属性不可为空。当对关系进行更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。

我需要找到一个解决方案,如何从数据库中删除不相关的房间。我应该在何时何地做?我已经尝试了一些东西,但现在即使我正在添加房间,我也会遇到这个错误。

有什么简单的方法告诉EF它应该自己删除这些房间吗?

【问题讨论】:

    标签: c# entity-framework asp.net-mvc-4 one-to-many


    【解决方案1】:

    只有在使用标识关系时,EF 才能自动删除孤儿。您需要为Room 对象定义一个复合键,它由IdHouseID 组成。

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

    使用此配置,EF 知道,当您从 Rooms 集合中删除房间对象时,也可以安全地删除数据库的房间。

    【讨论】:

    • 创建一个能够从父母中删除孩子的复合键太多了。然后,EF 仍然不会删除房间,因为它会尝试使 FK 值无效,或者,如果它不可为空,则将其替换为新值。
    • 您的说法不正确。如果您使用识别关系,即与复合键的关系,其中外键是主键的一部分,则自动删除将起作用。如果你从父集合中删除一个房间,EF 知道,如果没有它的父房子,这个房间就不能存在,它会从数据库中删除这个房间。
    • 好吧,你可能是对的。我仍然不会创建一个复合键来适应 ORM 实现。复合键是笨拙的东西。
    【解决方案2】:

    删除您不再希望单独从模型中显式删除它们的房间 - 相当于这个:

    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
    }
    

    【讨论】:

      【解决方案3】:

      假设您设置的 1 个房子当前有 4 个房间与之关联,这应该可以工作:

      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();
      

      【讨论】:

        猜你喜欢
        • 2015-08-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-06
        相关资源
        最近更新 更多