Is it possible to remove child from collection and resolve issues on SaveChanges?

GraemeMiller picture GraemeMiller · Jun 14, 2012 · Viewed 19.8k times · Source

We are using Entity Framework Code First with Foreign Key relationships. We investigating on ways on handling removing objects from an entities ICollection in our application.

When we have an entity with child relationships we can add objects directly to their ICollection using Add method. Now when you use remove you get the error

System.InvalidOperationException occurred Message=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 understand this is because Remove on the collection only deletes the relationship by nulling the foreign key. We wanted to write our business logic in our entity and allow removal.

So get root entity out out of it's Repostiory e.g Order from OrderRepository then call some specific method of the entity e.g. Order.AddOrderline(Orderline orderline) This adds an OrderLine to the Orders virtual ICollection<OrderLine> OrderLines

However we can't write code like Order.CancelOrderline(int orderLineId) because simply removing from the ICollection causes an error on savings changes.

There doesn't seem to be anyway to achieve this by just manipulating the object collections. Obviously we can remove directly from Context. However I'd like to make it part of the entity. Can we clean up certain entities with no foreign key on SaveChanges event of Entity Framework? Obviously need to tell EF what entities can be removed if they have null foreign key.

We presently are using a repository pattern so the controller doesn't have access to the context. I could obviously use an OrderLine repository or a remove OrderLine method on the Order repository. However just wondering if it was possible to write the code on the entity without references to the persistence mechanism.

Thoughts? Are we going about this all wrong? Do other ORMs allow you to just remove from Child Collections?

Answer

Slauma picture Slauma · Jun 14, 2012

I don't know if the following is a solution for you but Entity Framework supports Identifying Relationships. In such a relationship the foreign key of the child entity (dependent) to the parent (principal) must be part of the (composite) primary key of the child entity. For example - with DbContext data annotations - your model classes have to look like this:

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

You can make the OrderLineId an autogenerated identity if you want. Important is only that the FK to Order is part of the PK.

A code like this for example...

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

...would indeed delete the orderLineToDelete from the database.

More details are here in section "Considerations for Identifying and Non-identifying Relationships".