quand j'essaie d'attacher une entité au contexte, je reçois une exception
Un objet avec la même clé existe déjà dans ObjectStateManager. ObjectStateManager ne peut pas suivre plusieurs objets avec la même clé
Il s'agit d'un comportement attendu.
Mais je voudrais savoir comment ObjectStateManager sait cela? Je voudrais faire ce contrôle moi-même avant
Si vous utilisez DbContext API (vous avez mentionné ef-code-first), vous pouvez simplement utiliser:
context.YourEntities.Local.Any(e => e.Id == id);
ou plus complexe
context.ChangeTracker.Entries<YourEntity>().Any(e => e.Entity.Id == id);
Dans le cas de l'API ObjectContext, vous pouvez utiliser:
context.ObjectStateManager.GetObjectStateEntries(~EntityState.Detached)
.Where(e => !e.IsRelationship)
.Select(e => e.Entity)
.OfType<YourEntity>()
.Any(x => x.Id == id);
Voici une méthode d'extension pour extraire l'objet du contexte sans avoir à se soucier s'il est déjà attaché:
public static T GetLocalOrAttach<T>(this DbSet<T> collection, Func<T, bool> searchLocalQuery, Func<T> getAttachItem) where T : class
{
T localEntity = collection.Local.FirstOrDefault(searchLocalQuery);
if (localEntity == null)
{
localEntity = getAttachItem();
collection.Attach(localEntity);
}
return localEntity;
}
Il suffit d'appeler:
UserProfile user = dbContext.UserProfiles.GetLocalOrAttach<UserProfile>(u => u.UserId == userId, () => new UserProfile { UserId = userId });
vérifier
entity.EntityState == System.Data.EntityState.Detached
avant de fixer
Notez que si changement de suivi est désactivé sur votre contexte, demander au ObjectStateManager
ou au ChangeTracker
pourrait retourner que l'objet n'est pas dans le ObjectContext
même s'il est en fait déjà là-dedans. Par conséquent, si vous essayez d'attacher un tel objet, il déclenchera une exception.
context.Set<T>.Local.Any(e => e.Id == id);
fonctionne si le suivi des modifications est désactivé.
si vous ne connaissez pas le type de l'objet, il existe différentes approches, soit vous définissez une méthode utilisant la réflexion ou d'autres techniques comme celle-ci int GetIdOf(object entity){...}
Ou vous définissez une interface utilisée par vos classes comme
public interface IMyEntity
{
int Id{get;set;}
}
et utilisez-le de cette façon:
context.Set(e.GetType()).Local.Cast<IMyEntity>().Any(e => e.Id == id);
vous pouvez interroger le dbContext avec la méthode d'extension "Any":
bool alreadyInDB = dbContext.Entity.Where(a=>a.ID==myEntity.id).Any();