2012-07-26 16 views
14

Ho problemi con rilevando i cambiamenti di una proprietà di navigazione:Entity Framework non rileverà i cambiamenti di proprietà di navigazione

Il mio modello di prova si presenta così:

public class Person 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public virtual Address Address { get; set; } 
} 

public class Address 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 
} 

che ho creato e salvato un oggetto di tipo Persona con entrambe le proprietà Nome e Indirizzo assegnate. Il mio problema è che se recupero l'oggetto Person dal database e cambio la proprietà Address (ad esempio Null), allora l'e.f. non rileva il cambiamento! Il mio codice è questo:

using (var ctx = new EFContext()) 
{ 
    Person p = ctx.People.First(); 
    //p.Address IS NOT NULL! 
    p.Address = null; 
    var entry = ctx.Entry(p); 
} 

Perché entry.StateInvariato?

Modifica: Se chiamo SaveChanges, il record viene salvato correttamente (l'indirizzo diventa null)!

Edit 2: ho creato la proprietà chiave esterna come suggerito billy e se io ispezionare l'oggetto Person in Visual Studio viene modificato lo Stato .. se non mi fermo con il debugger ispezionare valori dell'oggetto i lo stato è invariato!

Modifica 3: Caricamento dell'oggetto Person utilizzando ctx.People.Include (x => x.Address) .First(); risolve il problema C'è un modo per evitare di chiamare Includi e continuare a modificare la proprietà Address invece di quella AddressId?

+0

Cosa succede se chiami ctx.DetectChanges()? – Maarten

+0

Niente. Il risultato è lo stesso!! – Mones

risposta

24

Prima di tutto: bisogna seguire @ consiglio di Billy da usare Include. La tua osservazione "p.Address IS NOT NULL!" è vera solo perché stai guardando p.Address nel debugger e quindi attivando il caricamento lento nel debugger, quindi viene rilevata la modifica dell'impostazione dell'indirizzo su null. Nella modalità di rilascio o quando non si ispezionano le proprietà nel debugger, il codice non funzionerebbe e non verrebbero salvate modifiche.

Quindi, la risposta alla tua Edit 3 è: No.

Secondo: var entry = ctx.Entry(p) restituisce solo entità afferma e non è stato modificato uno stato di entità, ma piuttosto uno stato rapporto, o più precisamente si eliminato una relazione.Non è possibile controllare gli stati di relazione con il DbContext API, ma solo con il ObjectContext API:

Person p = ctx.People.Include(x => x.Address).First(); 
p.Address = null; 
var objCtx = ((IObjectContextAdapter)ctx).ObjectContext; 
var objentr = objCtx.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted); 

objentr avrà una voce di tipo RelationshipEntry ora:

enter image description here

EF prenderà in considerazione questa voce rapporto insieme alle voci dello stato dell'entità quando si chiama SaveChanges() e si elimina la relazione, ovvero si imposta la colonna chiave esterna Address dello Person nel database su NULL.

Informazioni su Modifica 2: la modifica di una proprietà di chiave esterna (che è una proprietà scalare nel modello) è una modifica dell'entità stessa, pertanto lo stato dell'entità sarà Modified in questo caso.

+0

Wow! grazie davvero buona risposta! –

+0

Bella risposta, ma ho un dubbio. Come faccio a identificare che una voce specifica ha subito queste modifiche e non altre, poiché si tratta di una query per il contesto specifico. –

+0

Come si fa in questo in EF Core, perché non c'è 'ObjectContext'? – grokky

5

È necessario includere l'indirizzo nav. puntello. nella query, altrimenti EF non prenderà in considerazione le modifiche ad esso quando si salva:

using (var ctx = new EFContext()) 
{ 
    Person p = ctx.People.Include(x => x.Address).First(); 
    //p.Address IS NOT NULL! 
    p.Address = null; 
    var entry = ctx.Entry(p); 
} 

Si potrebbe anche usare chiavi esterne nel modello, che mi piace molto:

public class Person 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public virtual Address Address { get; set; } 

    public int? AddressId {get; set;} 
} 

...

using (var ctx = new EFContext()) 
{ 
    Person p = ctx.People.First(); 
    p.AddressId = null; 
    var entry = ctx.Entry(p); 
} 
+0

Ho provato la soluzione con DetectChanges ma in nessun modo ... ho ancora lo stato Unchanged! Se utilizzo l'int? Chiave strana Immagino che funzionerà correttamente perché cambierò il valore di una proprietà "semplice" e non direttamente la proprietà di navigazione. Infatti se cambio la proprietà Name dell'oggetto Person tutto è OK! – Mones

2

Nella mia applicazione, prima che venga richiesta una ricarica o l'utente lasci la voce/vista, eseguo alcuni controlli per assicurarmi che non ci siano modifiche non salvate.

Questo è fondamentalmente in esecuzione fuori la risposta attualmente accettato, ma ho voluto fornire un'implementazione e portare a conoscenza che è necessario chiamare Context.ChangeTracker.DetectChanges() prima della ObjectContext.ObjectStateManager può raccogliere i cambiamenti di rapporto! Ho passato un bel po 'di tempo a fare il debugging, sciocco!

_EagleContext.ChangeTracker.DetectChanges(); 

var objectContext = ((IObjectContextAdapter)_EagleContext).ObjectContext; 
var changedEntities = objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified); 

if (_EagleContext.ChangeTracker.Entries().Any(e => e.State == EntityState.Modified) 
    || changedEntities.Count() != 0) 
{ 
    var dialogResult = MessageBox.Show("There are changes to save, are you sure you want to reload?", "Warning", MessageBoxButton.YesNo); 
    if (dialogResult == MessageBoxResult.No) 
    { 
     return; 
    } 
} 

// Continue with reloading... 
Problemi correlati