Entity Framework Core: remove a relationship but not delete the entities

asp.net-core c# entity-framework-core

Question

How would you delete a relationship assuming you had the 2 entities, but did not have the 'relationship' entity?

Assuming the following entities...

Model classes:

public class DisplayGroup
{
        [Key]
        public int GroupId { get; set; }
        public string Description { get; set; }
        public string Name { get; set; }
        public ICollection<LookUpGroupItem> LookUpGroupItems { get; set; }
}

public class DisplayItem
{
        [Key]
        public int ItemId { get; set; }
        public string Description { get; set; }
        public string FileType { get; set; }
        public string FileName { get; set; }
        public ICollection<LookUpGroupItem> LookUpGroupItems { get; set; }
}

public class LookUpGroupItem
{
        public int ItemId { get; set; }
        public DisplayItem DisplayItem { get; set; }
        public int GroupId { get; set; }
        public DisplayGroup DisplayGroup { get; set; }
}

Here is the code for deleting a relationship. Note: I do not want to delete the entities, they just no longer share a relationship.

public void RemoveLink(DisplayGroup g, DisplayItem d)
{
    _dataContext.Remove(g.LookUpGroupItems.Single(x => x.ItemId == d.ItemId));
}

The method above causes an error:

System.ArgumentNullException occurred
Message=Value cannot be null.

It looks like this is the case because LookUpGroupItems is null, but these were called from the database. I would agree that I do not want to load all entity relationship objects whenever I do a Get from the database, but then, what is the most efficient way to do this?

Additional NOTE: this question is not about an argument null exception. It explicitly states how to delete a relationship in Entity Framework Core.

1
2
12/3/2017 9:07:33 AM

Popular Answer

The following is not the most efficient, but is the most reliable way:

public void RemoveLink(DisplayGroup g, DisplayItem d)
{
    var link = _dataContext.Find<LookUpGroupItem>(g.GroupId, d.ItemId); // or (d.ItemId, g.GroupId) depending of how the composite PK is defined
    if (link != null)
        _dataContext.Remove(link);
}

It's simple and straightforward. Find method is used to locate the entity in the local cache or load it the from the database. If found, the Remove method is used to mark it for deletion (which will be applied when you call SaveChanges).

It's not the most efficient because of the database roundtrip when the entity is not contained in the local cache.

The most efficient is to use "stub" entity (with only FK properties populated):

var link = new LookUpGroupItem { GroupId = g.GroupId, ItemId = d.ItemId };
_dataContext.Remove(link);

This will only issue DELETE SQL command when ApplyChanges is called. However it has the following drawbacks:

(1) If _dataContext already contains (is tracking) a LookUpGroupItem entity with the same PK, the Remove call will throw InvalidOperationException saying something like "The instance of entity type 'LookUpGroupItem' cannot be tracked because another instance with the key value 'GroupId:1, ItemId:1' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached."

(2) If database table does not contain a record with the specified composite PK, the SaveChanges will throw DbUpdateConcurrencyException saying "Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions." (this behavior is actually considered a bug by many people including me, but this is how it is).

Shorty, you can use the optimized method only if you use short lived newly create DbContext just for that operation and you are absolutely sure the record with such PK exists in the database. In all other cases (and in general) you should use the first method.

2
12/3/2017 4:24:29 PM


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