1

实体模型:

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

尝试更新现有文档时,出现以下错误:

无法跟踪实体类型“DocumentTypeRetractRelation”的实例,因为已在跟踪具有相同键的此类型的另一个实例。添加新实体时,对于大多数键类型,如果没有设置键(即,如果键属性被分配了其类型的默认值),将创建一个唯一的临时键值。如果您为新实体显式设置键值,请确保它们不会与现有实体或为其他新实体生成的临时值发生冲突。附加现有实体时,请确保只有一个具有给定键值的实体实例附加到上下文。

4

1 回答 1

2

问题不在于自引用,而是应用多对多集合修改,这些修改生成DocumentTypeRetractRelation具有相同 PK 的不同对象,如异常消息中所述。

当前在 EF Core 中的正确方法是确保RetractDocumentTypesexistingDocument加载(包含原始值),然后通过使用现有对象或创建新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收集内容)。所以只需将 替换RetractDocumentTypesDocumentTypes

于 2017-04-13T11:49:42.930 回答