Je suis conscient qu'une telle question a déjà été posée, mais la solution ne m'a pas aidé.
[Fact]
public async Task UpdateAsync()
{
string newTitle = "newTitle1";
int newBrandId = 3;
var item = await storeContext.Items.AsNoTracking().FirstOrDefaultAsync();
item.BrandId = newBrandId;
item.Title = newTitle;
storeContext.Entry(item).State = EntityState.Detached;
await service.UpdateAsync(item); // exception inside
var updatedItem = await storeContext.Items.AsNoTracking().FirstOrDefaultAsync();
Assert.Equal(newTitle, updatedItem.Title);
Assert.Equal(newBrandId, updatedItem.BrandId);
}
public async Task UpdateAsync(T entity)
{
_dbContext.Entry(entity).State = EntityState.Modified; // exception when trying to change the state
await _dbContext.SaveChangesAsync();
}
Message: System.InvalidOperationException: l'instance du type d'entité 'Item' ne peut pas être suivie car une autre instance avec la même valeur de clé pour {'Id'} est déjà suivie. Lorsque vous attachez des entités existantes, assurez-vous qu'une seule instance d'entité avec une valeur de clé donnée est attachée. Pensez à utiliser "DbContextOptionsBuilder.EnableSensitiveDataLogging" pour voir les valeurs de clé en conflit.
intéressant que l'exception soit la même même si aucun élément ne provient de db, comme ça
//var item = await storeContext.Items.AsNoTracking().FirstOrDefaultAsync();
var item = new Item()
{
Id = 1,
BrandId = newBrandId,
CategoryId = 1,
MeasurementUnitId = 1,
StoreId = 1,
Title = newTitle
};
De nombreux problèmes que j'ai rencontrés ont une racine désagréable. En bref: j'ai appris à la dure pourquoi dbContext est délimité plutôt que singleton. Voici le type de magasin, mais le problème était le même. Voici le code d'initialisation du test simplifié
public TestBase()
{
services = new ServiceCollection();
storeContext = StoreContextMock.ConfigureStoreContext(services, output);
serviceProvider = services.BuildServiceProvider();
}
public static StoreContext ConfigureStoreContext(IServiceCollection services)
{
services.AddDbContext<StoreContext>(c =>
c.UseInMemoryDatabase(Guid.NewGuid().ToString()).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));
var serviceProvider = services.BuildServiceProvider();
var storeContext = serviceProvider.GetRequiredService<StoreContext>();
storeContext .Stores.Add(new Store { Title = "John's store", Address = "NY", Description = "Electronics best deals", SellerId = "[email protected]" });
storeContext .Stores.Add(new Store { Title = "Jennifer's store", Address = "Sydney", Description = "Fashion", SellerId = "[email protected]" });
storeContext .SaveChanges();
return storeContext ;
}
J'ai relu l'erreur et j'ai finalement remarqué le mot principal
Impossible de suivre l'instance du type d'entité 'Store' car une autre instance avec la même valeur de clé pour {'Id'} est déjà suivie
Il doit donc y avoir une instance de suivi Orphan qui m'empêche de travailler avec le magasin. Je n'ai enregistré aucune référence à s1
ou s2
, il doit donc être storeContext
stocker les références sur les objets insérés même après avoir quitté la portée de leur déclaration et de leur initialisation. C'est pourquoi je n'ai pas pu mettre à jour les variables normalement et aussi pourquoi mes "requêtes" des objets db ont toutes leurs propriétés de navigation assignées (le chargement paresseux n'a pas grand-chose à voir avec cela). Le code suivant a résolu tous mes problèmes.
public static StoreContext ConfigureStoreContext(IServiceCollection services)
{
services.AddDbContext<StoreContext>(c =>
c.UseInMemoryDatabase(Guid.NewGuid().ToString()).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));
var serviceProvider = services.BuildServiceProvider();
var storeContext = serviceProvider.GetRequiredService<StoreContext>();
var s1 = new Store { Title = "John's store", Address = "NY", Description = "Electronics best deals", SellerId = "[email protected]" };
var s2 = new Store { Title = "Jennifer's store", Address = "Sydney", Description = "Fashion", SellerId = "[email protected]" }
storeContext .Stores.Add(s1);
storeContext .Stores.Add(s2);
storeContext .Entry<Store>(s1).State = EntityState.Detached;
storeContext .Entry<Store>(s2).State = EntityState.Detached;
storeContext .SaveChanges();
return storeContext ;
}
C'est l'une des nombreuses raisons pour lesquelles dbContext devrait être limité par une portée. Merci pour le indice .
J'ai eu le même problème lors de la copie de certains enregistrements dans la base de données par Entity Framework et en changeant une colonne qui était la clé d'entité de l'autre. Le changement de mode de suivi n'a pas résolu le problème.
Le problème a été résolu en définissant correctement la clé primaire dans EntityTypeConfiguration, pour contenir la valeur modifiée décrite ici comme x.EntityTwoKey.
builder.HasKey(x => new { x.EntityOneKey, x.EntityTwoKey });