是否可以从收集中删除子项并解决SaveChanges上的问题?

Gra*_*ler 21 persistence entity-framework repository-pattern entity-framework-4

我们正在使用Entity Framework Code First和Foreign Key关系.我们正在调查处理从应用程序中的实体ICollection中删除对象的方法.

当我们有一个具有子关系的实体时,我们可以使用Add方法将对象直接添加到他们的ICollection中.现在,当您使用删除时,您会收到错误

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

我理解这是因为集合上的Remove仅通过归零外键来删除关系.我们想在我们的实体中编写业务逻辑并允许删除.

所以从它的Repostiory中取出root实体,例如OrderRepository的Order然后调用实体的一些特定方法,例如,Order.AddOrderline(Orderline orderline)这将OrderLine添加到Ordersvirtual ICollection<OrderLine> OrderLines

但是,我们无法编写代码,Order.CancelOrderline(int orderLineId)因为只是从ICollection中删除会导致存储更改出错.

似乎没有任何方法可以通过操纵对象集合来实现这一点.显然我们可以直接从Context中删除.但是我想把它作为实体的一部分.我们可以在Entity Framework的SaveChanges事件中清除没有外键的某些实体吗?显然需要告诉EF如果它们具有空外键,可以删除哪些实体.

我们目前正在使用存储库模式,因此控制器无法访问上下文.我显然可以在Order存储库上使用OrderLine存储库或删除OrderLine方法.然而,只是想知道是否有可能在实体上编写代码而不引用持久性机制.

思考?我们这一切都错了吗?其他ORM是否允许您从Child Collections中删除?

Sla*_*uma 39

我不知道以下是否适合您,但实体框架支持识别关系.在这种关系中,子实体(从属)与父(主体)的外键必须是子实体的(复合)主键的一部分.例如 - 使用DbContext数据注释 - 您的模型类必须如下所示:

public class Order
{
    [Key]
    public int OrderId { get; set; }

    public ICollection<OrderLine> OrderLines { get; set; }
}

public class OrderLine
{
    [Key, ForeignKey("Order"), Column(Order = 1)]
    public int OrderId { get; set; }

    [Key, Column(Order = 2)]
    public int OrderLineId { get; set; }

    public Order Order { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

OrderLineId如果需要,您可以创建自动生成的标识.重要的只是FK Order是PK的一部分.

像这样的代码例如......

using (var ctx = new MyContext())
{
    var order = ctx.Orders.Include("OrderLines").Single(o => o.OrderId == 1);
    var orderLineToDelete = order.OrderLines
        .FirstOrDefault(ol => ol.OrderLineId == 5);
    if (orderLineToDelete != null)
        order.OrderLines.Remove(orderLineToDelete);

    ctx.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)

......的确会删除orderLineToDelete从数据库中.

更多详细信息,这里的部分" 识别和非识别关系的思考 ".

  • 这很棒,但我需要添加[Key,Column(Order = 0),DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int OrderLineId {get; 组; 让它不要抱怨身份插入. (6认同)
  • 我不敢相信我不知道这件事!谢谢.我不得不说,EF涉及这种类型的错误消息非常混乱(隐藏在3级内部异常中).顺便说一句,我和@GraemeMiller有类似的问题,但我很流利,所以解决方案是:`this.HasKey(c => new {c.ChildPrimaryKey,c.ParentPrimaryKey});`和`this.Property(c)一起=> c.ChildPrimaryKey).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);` (2认同)