A razão do erro no código fornecido é a seguir.
Quando você cria uma entidade Ado banco de dados, sua propriedade Sé inicializada com uma coleção que contém dois novos registros B. Idde cada uma dessas novas Bentidades é igual a 0.
// This line of code reads entity from the database
// and creates new instance of object A from it.
var a = db.Set<A>().Single();
// When new entity A is created its field S initialized
// by a collection that contains two new instances of entity B.
// Property Id of each of these two B entities is equal to 0.
public ICollection<B> S { get; set; } = new List<B>() { new B {}, new B {} };
Após a execução da linha de var a = db.Set<A>().Single()coleção de código Sda entidade, Aela não contém Bentidades do banco de dados, porque DbContext Dbnão usa carregamento lento e não há carregamento explícito da coleção S. A entidade Acontém apenas novas Bentidades que foram criadas durante a inicialização da coleção S.
Quando você chama IsModifed = truea Sestrutura da entidade de coleção , tenta adicionar esses dois novos serviços Bao controle de alterações. Mas falha porque ambas as novas Bentidades têm o mesmo Id = 0:
// This line tries to add to change tracking two new B entities with the same Id = 0.
// As a result it fails.
db.Entry(a).Collection(x => x.S).IsModified = true;
Você pode ver no rastreamento da pilha que a estrutura da entidade tenta adicionar Bentidades ao IdentityMap:
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.ThrowIdentityConflict(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry, Boolean updateDuplicate)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetPropertyModified(IProperty property, Boolean changeState, Boolean isModified, Boolean isConceptualNull, Boolean acceptChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.SetFkPropertiesModified(InternalEntityEntry internalEntityEntry, Boolean modified)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.SetFkPropertiesModified(Object relatedEntity, Boolean modified)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.set_IsModified(Boolean value)
E a mensagem de erro também informa que ele não pode rastrear a Bentidade Id = 0porque outra Bentidade com o mesmo Idjá está rastreada.
Como resolver este problema.
Para resolver esse problema, você deve excluir o código que cria Bentidades ao inicializar a Scoleção:
public ICollection<B> S { get; set; } = new List<B>();
Em vez disso, você deve preencher a Scoleção no local em que Aé criada. Por exemplo:
db.Add(new A {S = {new B(), new B()}});
Se você não usar o carregamento lento, deverá carregar explicitamente a Scoleção para adicionar seus itens ao rastreamento de alterações:
// Use eager loading, for example.
A a = db.Set<A>().Include(x => x.S).Single();
db.Entry(a).Collection(x => x.S).IsModified = true;
Por que não adiciona, em vez de anexar, as instâncias de B?
Em suma , eles são anexados a serem adicionados porque têm Detachedestado.
Depois de executar a linha de código
var a = db.Set<A>().Single();
instâncias criadas da entidade Btêm estado Detached. Pode ser verificado usando o próximo código:
Console.WriteLine(db.Entry(a.S[0]).State);
Console.WriteLine(db.Entry(a.S[1]).State);
Então, quando você define
db.Entry(a).Collection(x => x.S).IsModified = true;
O EF tenta adicionar Bentidades para alterar o rastreamento. No código-fonte do EFCore, você pode ver que isso nos leva ao método InternalEntityEntry.SetPropertyModified com os próximos valores de argumento:
property- uma das nossas Bentidades,
changeState = true,
isModified = true,
isConceptualNull = false,
acceptChanges = true.
Esse método com esses argumentos altera o estado dos Detached Batributos para Modifiede tenta iniciar o rastreamento deles (consulte as linhas 490 - 506). Como as Bentidades agora têm estado, Modifiedisso as leva a serem anexadas (não adicionadas).