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

114

Quelle est la meilleure façon de vérifier si un objet existe dans la base de données du point de vue des performances? J'utilise Entity Framework 1.0 (ASP.NET 3.5 SP1).

Freddy
la source

Réponses:

228

Si vous ne souhaitez pas exécuter SQL directement, le meilleur moyen est d'utiliser Any () . C'est parce que Any () reviendra dès qu'il trouvera une correspondance. Une autre option est Count () , mais cela peut nécessiter de vérifier chaque ligne avant de revenir.

Voici un exemple de son utilisation:

if (context.MyEntity.Any(o => o.Id == idToMatch))
{
    // Match!
}

Et dans vb.net

If context.MyEntity.Any(function(o) o.Id = idToMatch) Then
    ' Match!
End If
Alex Angas
la source
Et dans VB If (context.MyEntity.Any (o => o.Id <> idToMAtch)) Alors 'C'est un match! Fin Si Désolé, ce n'est pas dans la balise de code, je n'ai pas compris comment le faire!
Kevin Morrissey
Je pense que vous voulez dire o.Id <> idToMatch n'est PAS égal à un match
Colin
Que faire si je recherche par nom et que je veux obtenir l'ID s'il existe?
Mihai Bratulescu
salut. comment pouvons-nous vérifier s'il existe et ensuite sélectionner toutes ses données?
virtouso
1
@barnes Si vous vous contraignez Tà une interface qui est IEnumerableet retournez des objets qui contiennent un Id, vous devriez pouvoir utiliser votre fonction générique IsExists<T>().
Suncat2000
5

J'ai dû gérer un scénario où le pourcentage de doublons fournis dans les nouveaux enregistrements de données était très élevé et où des milliers d'appels à la base de données étaient effectués pour vérifier les doublons (le processeur a donc envoyé beaucoup de temps à 100%). Finalement, j'ai décidé de garder les 100 000 derniers enregistrements en mémoire cache. De cette façon, je pouvais vérifier les doublons par rapport aux enregistrements mis en cache, ce qui était extrêmement rapide par rapport à une requête LINQ sur la base de données SQL, puis écrire tous les enregistrements véritablement nouveaux dans la base de données (ainsi que les ajouter au cache de données, ce que je triés et coupés pour garder sa longueur gérable).

Notez que les données brutes étaient un fichier CSV contenant de nombreux enregistrements individuels devant être analysés. Les enregistrements de chaque fichier consécutif (qui arrivaient à un taux d'environ 1 toutes les 5 minutes) se chevauchaient considérablement, d'où le pourcentage élevé de doublons.

En bref, si vous avez des données brutes horodatées, à peu près dans l'ordre, l'utilisation d'un cache mémoire peut aider à la vérification de la duplication des enregistrements.

ProfNimrod
la source
2
Souvent, nous, les développeurs, élaborons votre scénario avec quelques rebondissements. Je voudrais vous demander de traduire votre solution en C # afin que nous et de nombreux futurs développeurs en profitions. +1. J'aimerais que la solution soit étendue à un article de blog non plus! :)
sangam
3

Je sais que c'est un fil très ancien, mais juste au cas où quelqu'un comme moi aurait besoin de cette solution, mais dans VB.NET, voici ce que j'ai utilisé en fonction des réponses ci-dessus.

Private Function ValidateUniquePayroll(PropertyToCheck As String) As Boolean
    // Return true if Username is Unique
    Dim rtnValue = False
    Dim context = New CPMModel.CPMEntities
    If (context.Employees.Any()) Then ' Check if there are "any" records in the Employee table
        Dim employee = From c In context.Employees Select c.PayrollNumber ' Select just the PayrollNumber column to work with
        For Each item As Object In employee ' Loop through each employee in the Employees entity
            If (item = PropertyToCheck) Then ' Check if PayrollNumber in current row matches PropertyToCheck
                // Found a match, throw exception and return False
                rtnValue = False
                Exit For
            Else
                // No matches, return True (Unique)
                rtnValue = True
            End If
        Next
    Else
        // The is currently no employees in the person entity so return True (Unqiue)
        rtnValue = True
    End If
    Return rtnValue
End Function
Kevin Morrissey
la source
Je ne sais pas comment utiliser Lambda en VB mais en C # c'est équivalent: return! Context.Employees.Any (c => c.PayrollNumber == PropertyToCheck). Cela évite de renvoyer tous les résultats puis de boucler en mémoire.
Colin
1
@Colin c'est un bon ajout J'ai négligé le problème de mémoire avec le code ci-dessus, dans VB le code est context.Employees.Any (c => c.PayrollNumber <> PropertyToCheck). J'ai maintenant ajouté ceci à mon code.
Kevin Morrissey
Kevin, je pense que vous devrez peut-être revenir en arrière et corriger votre code. Votre logique retourne sûrement vrai s'il y a des numéros de paie qui ne correspondent pas, plutôt que vrai quand il n'y a pas de numéros de paie correspondants.
Colin
@Colin désolé vous avez raison, je fournissais une version VB à votre exemple seulement je n'ai pas beaucoup raison C # et je pensais que == n'était pas égal à donc mon VB <>.
Kevin Morrissey
1
@KevinMorrissey Je pense que Coling disait que vous devez mettre un "Non" devant "contexte". puisque "return Not context.Employees.Any (c => c.PayrollNumber = PropertyToCheck)" N'EST PAS (je le répète), N'EST PAS le même que "return context.Employees.Any (c <> c.PayrollNumber = PropertyToCheck)" . Voyez-vous mon point? L'utilisation de "return Any <>" signifie que si vous en trouvez un qui ne correspond pas à ce nombre (même s'il en existe un), il retournera vrai quoi qu'il arrive. Au lieu de cela, utiliser "Not [...]. Any =" ne retournera True que s'il ne trouve pas la ligne que vous recherchez! Voyez-vous la différence?
Erx_VB.NExT.Coder
2

J'ai eu quelques problèmes avec cela - ma EntityKey se compose de trois propriétés (PK avec 3 colonnes) et je ne voulais pas vérifier chacune des colonnes car ce serait moche. J'ai pensé à une solution qui fonctionne à tout moment avec toutes les entités.

Une autre raison à cela est que je n'aime pas attraper UpdateExceptions à chaque fois.

Un peu de réflexion est nécessaire pour obtenir les valeurs des propriétés clés.

Le code est implémenté comme une extension pour simplifier l'utilisation comme:

context.EntityExists<MyEntityType>(item);

Regarde:

public static bool EntityExists<T>(this ObjectContext context, T entity)
        where T : EntityObject
    {
        object value;
        var entityKeyValues = new List<KeyValuePair<string, object>>();
        var objectSet = context.CreateObjectSet<T>().EntitySet;
        foreach (var member in objectSet.ElementType.KeyMembers)
        {
            var info = entity.GetType().GetProperty(member.Name);
            var tempValue = info.GetValue(entity, null);
            var pair = new KeyValuePair<string, object>(member.Name, tempValue);
            entityKeyValues.Add(pair);
        }
        var key = new EntityKey(objectSet.EntityContainer.Name + "." + objectSet.Name, entityKeyValues);
        if (context.TryGetObjectByKey(key, out value))
        {
            return value != null;
        }
        return false;
    }
Sven
la source
1
J'aimerais ajouter un commentaire à ma réponse qui a maintenant presque 9 ans. Je pense que de nos jours, il existe des solutions et des possibilités beaucoup plus propres qu'il n'y en avait en 2010/2011 avec Entity Framwork 4. Je recommanderais donc d'arrêter de voter pour cette réponse, mais d'ajouter à la place une nouvelle / meilleure réponse ci-dessous.
Sven
Veuillez également garder à l'esprit que ma solution était une solution générique qui fonctionnait pour de nombreuses entités avec des clés composites de tables / entités existantes que je ne pouvais pas changer. Donc, au lieu de toujours interroger .Any (...) avec 3 propriétés clés, j'ai simplement appelé .EntityExists ().
Sven
2

Je viens de vérifier si l'objet est nul, cela fonctionne à 100% pour moi

    try
    {
        var ID = Convert.ToInt32(Request.Params["ID"]);
        var Cert = (from cert in db.TblCompCertUploads where cert.CertID == ID select cert).FirstOrDefault();
        if (Cert != null)
        {
            db.TblCompCertUploads.DeleteObject(Cert);
            db.SaveChanges();
            ViewBag.Msg = "Deleted Successfully";
        }
        else
        {
            ViewBag.Msg = "Not Found !!";
        }                           
    }
    catch
    {
        ViewBag.Msg = "Something Went wrong";
    }

la source
0

Pourquoi ne pas le faire?

var result= ctx.table.Where(x => x.UserName == "Value").FirstOrDefault();

if(result?.field == value)
{
  // Match!
}
Matheus Miranda
la source
Cela lèvera une exception de référence nulle car FirstOrDefault () retournera null s'il ne trouve pas de résultat. Je suppose que vous pouvez faire si (résultat? .Field == valeur) pour éviter cela.
ToDevAndBeyond
Cela peut être inutilement lent car il charge l'entité. Si tout ce que vous voulez faire est de vérifier qu'une clé existe ou non.
Douglas Gaskell
0

Meilleure façon de le faire

Indépendamment de ce qu'est votre objet et de la table de la base de données, la seule chose dont vous avez besoin est la clé primaire de l'objet.

Code C #

var dbValue = EntityObject.Entry(obj).GetDatabaseValues();
if (dbValue == null)
{
   Don't exist
}

Code VB.NET

Dim dbValue = EntityObject.Entry(obj).GetDatabaseValues()
If dbValue Is Nothing Then
   Don't exist
End If
Basilic
la source
Pourquoi deux réponses presque identiques? La différence est insignifiante. De plus, ce n'est certainement pas la meilleure façon de le faire. Cela n'a pas de sens d'extraire toutes les valeurs de la base de données uniquement pour vérifier si un enregistrement existe .
Gert Arnold le