Is there a way to find all entities whose relationships have been deleted?

I try not to have my business logic know the inner workings of my data layer and vice versa.

But Entity Framework makes it difficult. I can paste into the collection (in my business layer) without reference to the ObjectContext:

order.Containers.Add(new Container { ContainerId = containerId, Order = order }); 

And it saves well when it comes time to do SaveChanges() in the data layer.

But to remove an item from the collection, I need a reference to the ObjectContext. (I do # 1 in this guide to removing EF objects .) If I just do this:

  delContainers.ForEach(container => order.Containers.Remove(container)); 

Then, when I call SaveChanges() , I get an exception telling me that I need to delete the object, as well as the link.

So my options, as I see it, are:

  • Passing a delegate into my business logic, which will call the ObjectContext Delete method of an Entity Framework object.
  • Or (I hope) find a way to get all entities that have their link removed and actually delete them. (right before calling SaveChanges() in my data layer.)

Does anyone know how to do this?

UPDATE:

I tried this:

 // Add an event when Save Changes is called this.ObjectContext.SavingChanges += OnSavingChanges; 

...

 void OnSavingChanges(object sender, EventArgs e) { var objectStateEntries = ObjectContext.ObjectStateManager .GetObjectStateEntries(EntityState.Deleted); foreach (var objectStateEntry in objectStateEntries) { if (objectStateEntry.IsRelationship) { // Find some way to delete the related entity } } } 

But none, although I removed the link, the set of deleted items is empty.

(I tried to look at all the elements too, and my relationship is not there. Obviously, there is something fundamental that I'm not talking about ObjectStateManager about.)

+6
source share
2 answers

The correct solution for EF is clause 3. of the related product. This means spreading FK to the main object in PK for the dependent object. This will form something called an identity relationship that automatically deletes the dependent object when it is deleted from the parent object.

If you do not want to change your model and still want to achieve this persistently, you may not know, but this will only work for independent associations . Some initial implementation that works for at least my simple tested solution:

 public partial class YourObjectContext { public override int SaveChanges(SaveOptions options) { foreach (ObjectStateEntry relationEntry in ObjectStateManager .GetObjectStateEntries(EntityState.Deleted) .Where(e => e.IsRelationship)) { var entry = GetEntityEntryFromRelation(relationEntry, 0); // Find representation of the relation IRelatedEnd relatedEnd = entry.RelationshipManager .GetAllRelatedEnds() .First(r => r.RelationshipSet == relationEntry.EntitySet); RelationshipType relationshipType = relatedEnd.RelationshipSet.ElementType; if (!SkipDeletion(relationshipType)) { // Now we know that model is inconsistent and entity on many side must be deleted if (!(relatedEnd is EntityReference)) // related end is many side { entry = GetEntityEntryFromRelation(relationEntry, 1); } if (entry.State != EntityState.Deleted) { context.DeleteObject(entry.Entity); } } } return base.SaveChanges(); } private ObjectStateEntry GetEntityEntryFromRelation(ObjectStateEntry relationEntry, int index) { var firstKey = (EntityKey) relationEntry.OriginalValues[index]; ObjectStateEntry entry = ObjectStateManager.GetObjectStateEntry(firstKey); return entry; } private bool SkipDeletion(RelationshipType relationshipType) { return // Many-to-many relationshipType.RelationshipEndMembers.All( r => r.RelationshipMultiplicity == RelationshipMultiplicity.Many) || // ZeroOrOne-to-many relationshipType.RelationshipEndMembers.Any( r => r.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne); } } 

To make it work, your entities must be enabled to dynamically track changes (all properties must be virtual and the entity must be proxied), or you must manually call DetectChanges .

In the case of associations with foreign keys, the situation will probably be much worse, because you will not find any remote relations in the state manager. You will have to track changes to collections or keys manually and compare them to find discrepancies (I'm not sure how to do this in general). External IMHO key association requires an identifying relationship. Using the FK properties already means that you have included an additional persistence dependency in your model.

+4
source

One way is to write a change handler in your data layer:

  private void ContainersChanged(object sender, CollectionChangeEventArgs e) { // Check for a related reference being removed. if (e.Action == CollectionChangeAction.Remove) { Context.DeleteObject(e.Element); } } 

There are many places you can plug this into: in the constructor or repository of the get or SavingChanges or anywhere:

  entity.Containers.AssociationChanged += new CollectionChangeEventHandler(ContainersChanged); 

Now you can remove the association from another place, and it will be a "cascade" for the object.

+2
source

Source: https://habr.com/ru/post/903946/


All Articles