En el código de ejemplo a continuación, obtengo la siguiente excepción al hacer db.Entry(a).Collection(x => x.S).IsModified = true:
System.InvalidOperationException: 'La instancia del tipo de entidad' B 'no se puede rastrear porque ya se está rastreando otra instancia con el valor clave' {Id: 0} '. Al adjuntar entidades existentes, asegúrese de que solo se adjunte una instancia de entidad con un valor clave dado.
¿Por qué no agrega en lugar de adjuntar las instancias de B?
Curiosamente, la documentación de IsModifiedno especifica InvalidOperationExceptioncomo una posible excepción. Documentación inválida o un error?
Sé que este código es extraño, pero lo escribí solo para comprender cómo funciona ef core en algunos casos extraños de egde. Lo que quiero es una explicación, no una solución.
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
public class A
{
public int Id { get; set; }
public ICollection<B> S { get; set; } = new List<B>() { new B {}, new B {} };
}
public class B
{
public int Id { get; set; }
}
public class Db : DbContext {
private const string connectionString = @"Server=(localdb)\mssqllocaldb;Database=Apa;Trusted_Connection=True";
protected override void OnConfiguring(DbContextOptionsBuilder o)
{
o.UseSqlServer(connectionString);
o.EnableSensitiveDataLogging();
}
protected override void OnModelCreating(ModelBuilder m)
{
m.Entity<A>();
m.Entity<B>();
}
}
static void Main(string[] args)
{
using (var db = new Db()) {
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
db.Add(new A { });
db.SaveChanges();
}
using (var db = new Db()) {
var a = db.Set<A>().Single();
db.Entry(a).Collection(x => x.S).IsModified = true;
db.SaveChanges();
}
}
}
fuente

Respuestas:
La razón del error en el código proporcionado es la siguiente.
Cuando obtiene una entidad creada
Ade la base de datos, su propiedadSse inicializa con una colección que contiene dos nuevos registrosB.Idde cada una de estas nuevasBentidades es igual a0.Después de ejecutar la línea de
var a = db.Set<A>().Single()colección de códigoSde entidadA, no contieneBentidades de la base de datos, porqueDbContext Dbno utiliza carga diferida y no hay carga explícita de la colecciónS. La entidadAsolo contiene nuevasBentidades que se crearon durante la inicialización de la recopilaciónS.Cuando solicita
IsModifed = trueunSmarco de entidad de recopilación , intenta agregar esas dos nuevas entidadesBal seguimiento de cambios. Pero falla porque ambas nuevasBentidades tienen lo mismoId = 0:Puede ver en el seguimiento de la pila que el marco de la entidad intenta agregar
Bentidades enIdentityMap:Y el mensaje de error también dice que no puede rastrear la
BentidadId = 0porque otraBentidad con la mismaIdya está rastreada.Cómo resolver este problema.
Para resolver este problema, debe eliminar el código que crea
Bentidades al inicializar laSrecopilación:En su lugar, debe llenar la
Scolección en el lugar dondeAse crea. Por ejemplo:Si no utiliza la carga diferida, debe cargar explícitamente la
Scolección para agregar sus elementos al seguimiento de cambios:En resumen , se adjuntan en lugar de ser agregados porque tienen
Detachedestado.Después de ejecutar la línea de código
Las instancias creadas de entidad
Btienen estadoDetached. Se puede verificar usando el siguiente código:Entonces cuando configuras
EF intenta agregar
Bentidades para cambiar el seguimiento. Desde el código fuente de EFCore , puede ver que esto nos lleva al método InternalEntityEntry.SetPropertyModified con los siguientes valores de argumento:property- una de nuestrasBentidades,changeState = true,isModified = true,isConceptualNull = false,acceptChanges = true.Este método con tales argumentos cambia el estado de las
DetachedBentidades aModified, y luego intenta comenzar a rastrearlas (véanse las líneas 490 - 506). Debido a que lasBentidades ahora tienen estado,Modifiedesto los lleva a estar unidos (no agregados).fuente
Sdebe cargarse explícitamente, porque el código provisto no usa carga diferida. Por supuesto, EF guardóBentidades creadas previamente en la base de datos. Pero la línea de códigoA a = db.Set<A>().Single()solo carga entidadesAsin entidades en la colecciónS. Para cargar la colección, seSdebe utilizar una carga ansiosa. Cambiaré mi respuesta para incluir explícitamente la respuesta a la pregunta "¿Por qué no agrega en lugar de adjuntar las instancias de B?".