EF核心 - 自我引用多對多關係

c# entity-framework-core many-to-many self-referencing-table

實體模型:

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; }
}

關係模型:

/// <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; }
}

模型構建器:

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);

更新作者:

    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;
        }
    }

嘗試更新existingDocument時,我收到以下錯誤:

無法跟踪實體類型“DocumentTypeRetractRelation”的實例,因為已經跟踪了具有相同鍵的此類型的另一個實例。添加新實體時,對於大多數鍵類型,如果未設置任何鍵,則將創建唯一的臨時鍵值(即,如果為鍵屬性指定了其類型的默認值)。如果要為新實體顯式設置鍵值,請確保它們不會與現有實體或為其他新實體生成的臨時值發生衝突。附加現有實體時,請確保只有一個具有給定鍵值的實體實例附加到上下文。

一般承認的答案

問題不在於自引用,而是應用多對多集合修改,這些修改生成具有與異常消息中所述相同的PK的不同DocumentTypeRetractRelation對象。

正確的方法目前在英孚的核心是確保RetractDocumentTypes的的existingDocument被加載(包括原始值),然後合併使用現有的或創建新的或者改變DocumentTypeRetractRelation對象。

替換以下代碼

foreach (var retractDocumentTypeId in documentTypeUpdateDto.RetractDocumentTypeIds)
{
    existingDocument.RetractDocumentTypes.Add(new DocumentTypeRetractRelation()
    {
        DocumentTypeId = existingDocument.Id,
        RetractDocumentTypeId = retractDocumentTypeId
    });
}

// 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();

這將處理添加,刪除和未更改的關係。您可以執行與DocumentTypes類似的操作。

實際上看一下你的模型,上面的代碼應該是DocumentTypes集合(因為你接收了RetractDocumentTypeIds ,它與文檔Id結合形成DocumentTypes集合內容)。因此,只需將RetractDocumentTypes替換為DocumentTypes



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow