web-dev-qa-db-fra.com

Un moyen générique pour vérifier si l'entité existe dans Entity Framework?

Similaire à Meilleur moyen de vérifier si l'objet existe dans Entity Framework?

Je cherche un moyen générique de vérifier une entité dans un DbSet. Quelque chose comme ça, qui ne fonctionne pas:

private DbContext DbContext { get; set; }

private DbSet<T> DbSet { get; set; }

public Boolean Exists(T entity) {
    return ((from item in this.DbSet
             where item == entity
             select item).Count() > 0);
}

La ligne where item == entity fonctionne dans LINQ to SQL, mais apparemment pas avec LINQ to Entities. Étant donné que les entités peuvent avoir des clés différentes, je ne peux pas toutes les hériter d'un résumé commun avec une clé connue pour comparaison.

Je pourrais le faire, mais je m'inquiète des performances de la capture d'exceptions en tant que processus de vérification Cela ne fonctionne pas non plus car tant que l'entité est détachée, la propriété OriginalValues ne peut pas être obtenue:

public Boolean Exists(T entity) {
    try {
        var current = this.DbContext.Entry(entity).OriginalValues;
        // Won't reach this line if the entity isn't in the database yet
        return true;
    }
    catch (Exception ex) {
        return false;
    }
}
57
Yuck

Voulez-vous un moyen générique pour vérifier si l'entité a été chargée par le contexte ou un moyen générique pour interroger la base de données si l'entité existe?

Pour l'ancien cas, utilisez:

public bool Exists<T>(T entity) where T: class
{
    return this.Set<T>().Local.Any(e => e == entity);
}

Pour ce dernier cas, utilisez (il vérifiera également les entités chargées):

public bool Exists<T>(params object[] keys)
{
    return (this.Set<T>().Find(keys) != null);
}

Modifier:

Le code EF n'est pas censé d'abord accéder à ce type d'informations, mais il est possible d'obtenir le nom des clés d'entité. Je pense que quelque chose comme ça devrait fonctionner:

var objContext = ((IObjectContextAdapter)dbContext).ObjectContext;
var objSet = objContext.CreateObjectSet<T>();
var keyNames = objSet.EntitySet.ElementType.KeyMembers.Select(m => m.Name);

Mais tout cela n'a pas de sens. Vous voulez une approche générique mais vos entités ne partagent pas les informations nécessaires pour permettre une approche générique. Maintenant, vous dites que vous ne connaissez même pas les valeurs clés. L'utilisation de cette approche "générique" nécessitera une réflexion et une construction manuelle de l'arbre d'expression.

84
Ladislav Mrnka

Merci à @Ladislav de m'avoir mis dans la bonne direction. Voici le code d'une méthode générique Exists().

Je voudrais noter que cela ne nécessite pas de réflexion et semble fonctionner assez bien. La seule chose dont je ne suis pas ravi, c'est que TryGetObjectByKey() a pour effet secondaire d'attacher l'entité trouvée. Comme je ne veux pas que Exists() ait ce résultat involontaire, je dois détacher l'entité si elle a été trouvée.

public Boolean Exists(T entity) {
    var objContext = ((IObjectContextAdapter)this.DbContext).ObjectContext;
    var objSet = objContext.CreateObjectSet<T>();
    var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, entity);

    Object foundEntity;
    var exists = objContext.TryGetObjectByKey(entityKey, out foundEntity);
    // TryGetObjectByKey attaches a found entity
    // Detach it here to prevent side-effects
    if (exists) {
        objContext.Detach(foundEntity);
    }

    return (exists);
}
21
Yuck