Modello entità:
public class DocumentType : CodeBase
{
[Required]
[MaxLength(100)]
public string Name { get; set; }
public TimeSpan? Productiontime { get; set; }
public bool IsDeliverable { get; set; }
public virtual ICollection<DocumentTypeRetractRelation> DocumentTypes { get; set; }
public virtual ICollection<DocumentTypeRetractRelation> RetractDocumentTypes { get; set; }
}
Modello di relazione:
/// <summary>
/// Relationship between document types showing which documenttypes can
/// retracted when delivering a new document.
/// </summary>
[Table("DocumentTypeRetractRelation")]
public class DocumentTypeRetractRelation
{
public int DocumentTypeId { get; set; }
public virtual DocumentType DocumentType { get; set; }
public int RetractDocumentTypeId { get; set; }
public virtual DocumentType RetractDocumentType { get; set; }
}
Costruttore di modelli:
modelBuilder.Entity<DocumentTypeRetractRelation>().HasKey(x => new { x.DocumentTypeId, x.RetractDocumentTypeId });
modelBuilder.Entity<DocumentTypeRetractRelation>()
.HasOne(x => x.DocumentType)
.WithMany(x => x.DocumentTypes)
.HasForeignKey(x => x.DocumentTypeId);
modelBuilder.Entity<DocumentTypeRetractRelation>()
.HasOne(x => x.RetractDocumentType)
.WithMany(x => x.RetractDocumentTypes)
.HasForeignKey(x => x.RetractDocumentTypeId);
Aggiorna scrittore:
public async Task<DocumentType> UpdateAsync(DocumentTypeUpdateDto documentTypeUpdateDto)
{
using (IUnitOfWork uow = UowProvider.CreateUnitOfWork<EntityContext>())
{
var documentTypeRepo = uow.GetCustomRepository<IDocumentTypeRepository>();
var existingDocument = await documentTypeRepo.GetAsync(documentTypeUpdateDto.Id);
if (existingDocument == null)
throw new EntityNotFoundException("DocumentType", existingDocument.Id);
foreach (var retractDocumentTypeId in documentTypeUpdateDto.RetractDocumentTypeIds)
{
existingDocument.RetractDocumentTypes.Add(new DocumentTypeRetractRelation()
{
DocumentTypeId = existingDocument.Id,
RetractDocumentTypeId = retractDocumentTypeId
});
}
documentTypeRepo.Update(existingDocument);
await uow.SaveChangesAsync();
return existingDocument;
}
}
Quando si tenta di aggiornare il documento esistente, viene visualizzato il seguente errore:
L'istanza del tipo di entità "DocumentTypeRetractRelation" non può essere tracciata perché un'altra traccia di questo tipo con la stessa chiave è già stata tracciata. Quando si aggiungono nuove entità, per la maggior parte dei tipi di chiave verrà creato un valore di chiave temporanea univoco se non viene impostata alcuna chiave (cioè se alla proprietà della chiave viene assegnato il valore predefinito per il suo tipo). Se si impostano in modo esplicito valori chiave per le nuove entità, assicurarsi che non entrino in collisione con entità esistenti o valori temporanei generati per altre nuove entità. Quando si allegano entità esistenti, assicurarsi che solo una istanza di entità con un dato valore chiave sia allegata al contesto.
Il problema non è l'autoreferenzialità, ma l'applicazione delle modifiche della collezione many-to-many che generano diversi oggetti DocumentTypeRetractRelation
con lo stesso PK indicato nel messaggio di eccezione.
Il modo corretto attualmente in EF Core è di assicurarsi che i documenti RetractDocumentTypes
del documento existingDocument
siano caricati (contengano i valori originali), quindi unire le modifiche utilizzando uno esistente o creando nuovi oggetti DocumentTypeRetractRelation
.
Sostituisci il seguente codice
foreach (var retractDocumentTypeId in documentTypeUpdateDto.RetractDocumentTypeIds)
{
existingDocument.RetractDocumentTypes.Add(new DocumentTypeRetractRelation()
{
DocumentTypeId = existingDocument.Id,
RetractDocumentTypeId = retractDocumentTypeId
});
}
con
// existingDocument.RetractDocumentTypes should be loaded (either eager or explicit)
existingDocument.RetractDocumentTypes = (
from retractDocumentTypeId in documentTypeUpdateDto.RetractDocumentTypeIds
join existingRelation in existingDocument.RetractDocumentTypes
on retractDocumentTypeId equals existingRelation.RetractDocumentTypeId
into existingRelations
select existingRelations.FirstOrDefault() ?? new DocumentTypeRetractRelation()
{
DocumentTypeId = existingDocument.Id,
RetractDocumentTypeId = retractDocumentTypeId
}).ToList();
Questo gestiva entrambe le relazioni aggiunte, rimosse e invariate. Puoi fare simile a DocumentTypes
.
In realtà, guardando il tuo modello, il codice sopra dovrebbe essere per la raccolta DocumentTypes
(dato che hai ricevuto il RetractDocumentTypeIds
, che in combinazione con il documento Id
forma il contenuto della raccolta DocumentTypes
). Quindi semplicemente sostituire i DocumentTypes
RetractDocumentTypes
con DocumentTypes
.