Ho un'entità che ha un oggetto valore e questo oggetto valore ha un altro oggetto valore. Il mio problema è che quando si aggiorna l'entità insieme agli oggetti valore, l'entità con l'oggetto valore padre viene aggiornata ma l'oggetto valore figlio no. nota, ho usato l'ultima versione di Entity Framework Core 2.1.0-rc1-final, questa è l'entità dipendente Dipendente
public class Employee : Entity
{
public string FirstName { get; private set; }
public string LastName { get; private set; }
public string Email { get; private set; }
public Address Address { get; private set; }
}
e questo è l'indirizzo dell'oggetto valore genitore
public class Address : ValueObject<Address>
{
private Address() { }
public Address(string street, string city, string state, string country, string zipcode, GeoLocation geoLocation)
{
Street = street;
City = city;
State = state;
Country = country;
ZipCode = zipcode;
GeoLocation = geoLocation;
}
public string Street { get; private set; }
public string City { get; private set; }
public string State { get; private set; }
public string Country { get; private set; }
public string ZipCode { get; private set; }
public GeoLocation GeoLocation { get; private set; }
}
e questo è l'oggetto valore figlio GeoLocation
public class GeoLocation
{
private GeoLocation()
{
}
public GeoLocation(decimal longitude, decimal latitude)
{
Latitude = latitude;
Longitude = longitude;
}
public Decimal Longitude { get; private set; }
public Decimal Latitude { get; private set; }
}
e quando aggiorno il dipendente, prima lo prendo dal database, quindi cambio la proprietà Address usando il nuovo valore ottenuto dall'interfaccia utente
var employee = _repository.GetEmployee(empId);
employee.SetAddress(newAddress);
e il metodo SetAddress
public void SetAddress(Address address)
{
Guard.AssertArgumentNotNull(address, nameof(address));
Address = address;
}
In base a questo ticket EF Core GitHub, è necessario aggiornare direttamente le proprietà del tipo figlio / nidificato / posseduto affinché possa essere tracciato correttamente. Ciò avrebbe dovuto essere risolto in EF 2.1 (attualmente disponibile solo come candidato al rilascio) ma potrebbe non aver apportato il taglio. In 2.0.3 hanno aggiornato la verbosità dell'eccezione a:
InvalidOperationException: l'istanza del tipo di entità 'Parent.Child # Child' non può essere tracciata perché è già stata tracciata un'altra istanza con lo stesso valore chiave per {'ParentID'}. Quando si sostituiscono entità possedute, modificare le proprietà senza modificare l'istanza o staccare prima la voce dell'entità posseduta precedente.
La seconda parte di questo messaggio ti farà vomitare un po 'se stai usando DDD. Ti sta dicendo che devi aggiornare le proprietà delle proprietà figlio / nidificate direttamente per EF per tracciare correttamente le modifiche (che rompe gli oggetti valore DDD come immutabili). Come da un commento sul thread di GitHub, ecco una soluzione alternativa suggerita, un po 'DDD, adattata per abbinare il tuo codice:
public void SetAddress(Address address)
{
Guard.AssertArgumentNotNull(address, nameof(address));
Address.UpdateFrom(address);
}
// And on Address:
internal void UpdateFrom(Address other)
{
Street = other.Street;
// ...
}
-O-
La seconda soluzione suggerita viene eseguita staccando l'entità, aggiornando l'istanza di Address
, quindi ricollegandola. Non ho avuto molta fortuna con questa soluzione alternativa nella mia implementazione, ma la posterò per i posteri. Forse avrai più fortuna di me che di me.
context.Entry(employee.Address).State = EntityState.Detached;
employee.SetAddress(newAddress);
context.Entry(employee.Address).State = EntityState.Modified;
AGGIORNARE
Ho finalmente trovato il biglietto aperto con il team EF Core che può essere rintracciato per questo problema. Il biglietto n. 10551 indica specificamente il problema a portata di mano ed è ancora aperto. Sicuramente non è arrivato a EF Core 2.1 e sembra essere stato inserito nel Backlog Milestone 3.0. Si noti che è possibile votare questo problema in modo da indurre il team EF Core a dedicare maggiore attenzione al problema.
AGGIORNAMENTO 2 EF Core 2.2 ha introdotto un componente del grafico tracciato che lo rende molto più fluido. Ciò richiede tuttavia che tutte le entità EF utilizzino ID generati dal database. Questo metodo controlla se la chiave di entità è impostata, quindi contrassegna l'entità come modificata o aggiunta. Questo può essere espanso per includere le eliminazioni, ma per i miei scopi non voglio quel tipo di comportamento.
internal void Upsert(object entity)
{
ChangeTracker.TrackGraph(entity, e =>
{
if (e.Entry.IsKeySet)
{
e.Entry.State = EntityState.Modified;
}
else
{
e.Entry.State = EntityState.Added;
}
});
#if DEBUG
foreach (var entry in ChangeTracker.Entries())
{
Debug.WriteLine($"Entity: {entry.Entity.GetType().Name} State: {entry.State.ToString()}");
}
#endif
}
Quindi, utilizzare il context.Upsert(<YOUR ENTITY OBJECT>);
prima di context.SaveChanges();
.
Un'alternativa più semplice può essere trovata nella proprietà Tipo di proprietà che non persiste per un'entità modificata in EF Core
in breve, invece di
_context.Entry (contactModelFromRequestBody) .State = EntityState.Modified;
dovresti usare
_context.Update (contactModelFromRequestBody);