EF 4: Rimuovere l’object figlio dalla raccolta non lo elimina – perché?

Uso Entity Framework 4 e ho una relazione genitore-figlio con il set “Cascade Delete”. Quindi mi aspetterei che quando rimuovo un figlio dal genitore che il bambino viene cancellato quando chiamo SaveChanges ().

cuRepository.Attach(_controlUnit); foreach (var recipe in recipes) { _controlUnit.Recipes.Remove(recipe); //repository.DeleteObject(recipe); } 

Invece ottengo un errore:

System.InvalidOperationException received Message = L’operazione non è riuscita: la relazione non può essere modificata perché una o più proprietà della chiave esterna non sono annullabili. Quando viene apportata una modifica a una relazione, la relativa proprietà della chiave esterna viene impostata su un valore nullo. Se la chiave esterna non supporta valori nulli, deve essere definita una nuova relazione, la proprietà chiave esterna deve essere assegnata a un altro valore non nullo o l’object non correlato deve essere eliminato.

Quando elimini esplicitamente i bambini (vedi linea commentata), tutto va bene. Cosa mi manca?

Non stai cancellando l’object con l’istruzione remove. Invece si sta tentando di modificare un record e renderlo un orfano (impostando la chiave esterna su null). Il database ha un vincolo non nullo su quella colonna e ti impedisce di farlo.

http://weblogs.asp.net/zeeshanhirani/archive/2010/07/23/removing-entity-from-a-related-collection.aspx spiega esattamente cosa ti è successo.


Supponendo che tu abbia un design di class qualcosa del genere:

esempio di design di classe

Entity Framework genererà le colonne delle chiavi esterne richieste e aggiungerà vincoli NOT NULL perché tutte le Ricette saranno sempre associate esattamente a una ControlUnit.

Quindi in fase di esecuzione si avranno oggetti simili al seguente layout:

diagramma degli oggetti in fase di esecuzione

Ora il tuo codice entra in gioco ed elimina la relazione tra gli oggetti Recipe e ControlUnit:

oggetti con relazioni cancellate

Cercando di salvare in questo momento, il database non ha un ID ControlUnit da inserire nella chiave esterna NOT NULL colonna NOT NULL . Lo stato dell’object corrente viola il diagramma delle classi sopra riportato e non può essere salvato su un layout di database che è stato generato supponendo che ogni Ricetta sia associata a una ControlUnit. Questo è il motivo per cui il database si rifiuta di salvare le modifiche e si vede l’eccezione.

Questo spiega anche perché funziona quando disattivi la riga cancellando l’entity framework: l’ quadro viene rimossa dal database insieme alla sua relazione, quindi nessun vincolo viene violato, quindi nessuna eccezione.

“Ma ho impostato ON DELETE CASCADE sulla relazione …”

Sì, ma viene triggersto solo alla cancellazione dell’object, non alla cancellazione della relazione. Con il set ON DELETE CASCADE , questo dovrebbe funzionare:

 controlUnitRepository.DeleteObject(_controlUnit); // deletes the ControlUnit and all associated Recipe entities 

Se si desidera triggersre l’eliminazione delle quadro della ricetta all’eliminazione della loro relazione con ControlUnit, la relazione non deve essere una semplice associazione ma piuttosto una composizione:

diagramma di classe aggiornato con composizione

EF non supporta questo in modo nativo, ma è ansible emulare il comportamento utilizzando le relazioni di identificazione. Una volta che un’ quadro si trova in una relazione identificativa con un’entity framework padre e tale relazione viene rimossa, anche l’entity framework viene rimossa. Sembra che questa fosse la tua intenzione dall’inizio. Per ulteriori informazioni sull’identificazione della relazione, vedere Implementazione delle relazioni identificative con EF4 in cui ho implementato l’identificazione delle relazioni con EF4 e sono state collegate a più materiale di lettura.

aggiungi context.DeleteObject(recipe) all’interno del ciclo

Se rendi identificativo il rapporto tra bambino e genitore, puoi rimuovere le entity framework figlio dalla raccolta. È necessario rendere la chiave del bambino una chiave composta contenente la chiave id principale del genitore. In questo modo EF sa che è necessario rimuovere il bambino.

La relazione identificativa fondamentalmente dice che se il genitore non esiste, allora il bambino non ha significato. Ciò significa che EF sa che è sicuro eliminare il bambino quando la relazione viene rimossa.

Vedi questa domanda Identificare la relazione e inserire le quadro figlio causa “Imansible inserire il valore esplicito per la colonna Identity nella tabella” e questo È ansible rimuovere child dalla raccolta e risolvere i problemi su SaveChanges?

Uso questa estensione per non aggiungere un metodo in DAL solo per eliminare un’ quadro (codice preso da http://blogs.msdn.com/b/alexj/archive/2009/06/08/tip-24-how- to-get-the-objectcontext-from-an-entity.aspx ):

 public static void Delete(this EntityCollection collection, T entityToDelete) where T : EntityObject, IEntityWithRelationships { RelationshipManager relationshipManager = entityToDelete.RelationshipManager; IRelatedEnd relatedEnd = relationshipManager.GetAllRelatedEnds().FirstOrDefault(); if (relatedEnd == null) { throw new Exception("No relationships found for the entity to delete. Entity must have at least one relationship."); } var query = relatedEnd.CreateSourceQuery() as ObjectQuery; if (query == null) { throw new Exception("The entity to delete is detached. Entity must be attached to an ObjectContext."); } query.Context.DeleteObject(entityToDelete); collection.Remove(entityToDelete); } 

Quindi cancello quindi un’entity framework come Order.Products.Delete(prod) .

I vincoli per utilizzare l’estensione sono:
– L’ quadro deve avere relazioni;
– L’ quadro deve essere associata a ObjectContext.