Najlepszy sposób na sprawdzenie, czy obiekt istnieje w Entity Framework?

114

Jaki jest najlepszy sposób sprawdzenia, czy obiekt istnieje w bazie danych z punktu widzenia wydajności? Używam Entity Framework 1.0 (ASP.NET 3.5 SP1).

Freddy
źródło

Odpowiedzi:

228

Jeśli nie chcesz bezpośrednio wykonywać kodu SQL, najlepszym sposobem jest użycie Any () . Dzieje się tak, ponieważ Any () powróci, gdy tylko znajdzie dopasowanie. Inną opcją jest Count () , ale może to wymagać sprawdzenia każdego wiersza przed zwróceniem.

Oto przykład, jak go używać:

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

Oraz w vb.net

If context.MyEntity.Any(function(o) o.Id = idToMatch) Then
    ' Match!
End If
Alex Angas
źródło
A w VB If (context.MyEntity.Any (o => o.Id <> idToMAtch)) Then 'This is a match! Zakończ, jeśli przepraszam, tego nie ma w tagu kodu, nie mogłem wymyślić, jak to zrobić!
Kevin Morrissey,
Myślisz, że masz na myśli o.Id <> idToMatch NIE równa się dopasowaniu
Colin
co zrobić, jeśli wyszukuję według nazwy i chcę uzyskać identyfikator, jeśli istnieje?
Mihai Bratulescu
cześć. jak możemy sprawdzić, czy istnieje, a następnie wybrać wszystkie jego dane?
virtouso,
1
@barnes Jeśli ograniczasz się Tdo interfejsu, który jest IEnumerablei zwracasz obiekty, które zawierają znak Id, powinieneś być w stanie użyć swojej funkcji ogólnej IsExists<T>().
Suncat2000
5

Musiałem poradzić sobie ze scenariuszem, w którym procent duplikatów dostarczanych w nowych rekordach danych był bardzo wysoki i tak wiele tysięcy wywołań bazy danych było wykonywanych w celu sprawdzenia duplikatów (więc procesor wysyłał dużo czasu na 100%). Ostatecznie postanowiłem zachować ostatnie 100 000 rekordów w pamięci podręcznej. W ten sposób mogłem sprawdzić, czy nie ma duplikatów w zapisach w pamięci podręcznej, co było niezwykle szybkie w porównaniu z zapytaniem LINQ do bazy danych SQL, a następnie zapisać wszelkie autentycznie nowe rekordy do bazy danych (a także dodać je do pamięci podręcznej danych, co również posortowane i przycięte, aby zachować odpowiednią długość).

Zwróć uwagę, że nieprzetworzone dane były plikiem CSV zawierającym wiele pojedynczych rekordów, które trzeba było przeanalizować. Rekordy w każdym kolejnym pliku (które pojawiały się w tempie około 1 na 5 minut) znacznie się pokrywały, stąd wysoki procent duplikatów.

Krótko mówiąc, jeśli napływają surowe dane ze znacznikami czasu, prawie w porządku, użycie pamięci podręcznej może pomóc w sprawdzaniu duplikacji rekordów.

ProfNimrod
źródło
2
Wiele razy my, programiści, wymyślamy twój scenariusz, może być z pewnymi zwrotami akcji. Chciałbym poprosić Cię o przetłumaczenie rozwiązania na język C #, abyśmy my i wielu przyszłych programistów skorzystali na tym. +1. Bardzo chciałbym, aby rozwiązanie zostało rozszerzone do wpisu na blogu! :)
sangam
3

Wiem, że to bardzo stary wątek, ale po prostu na wypadek, gdyby ktoś taki jak ja potrzebował tego rozwiązania, ale w VB.NET oto, czego użyłem w oparciu o powyższe odpowiedzi.

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
źródło
Nie wiem, jak używać Lambda w VB, ale w C # jest to równoważne: return! Context.Employees.Any (c => c.PayrollNumber == PropertyToCheck). Pozwala to uniknąć zwracania wszystkich wyników i zapętlania ich w pamięci.
Colin
1
@Colin to dobry dodatek. Przeoczyłem problem z pamięcią w powyższym kodzie, w VB kod jest context.Employees.Any (c => c.PayrollNumber <> PropertyToCheck). Dodałem to teraz do mojego kodu.
Kevin Morrissey
Kevin, myślę, że będziesz musiał wrócić i naprawić swój kod. Twoja logika z pewnością zwraca prawdę, jeśli są jakieś niezgodne numery listy płac, a nie prawdę, gdy nie ma żadnych pasujących numerów na liście płac.
Colin
@Colin przepraszam, masz rację, dostarczałem wersję VB do twojego przykładu, ale nie poprawiłem zbyt wiele C # i pomyślałem, że == nie jest równe stąd mojemu VB <>.
Kevin Morrissey
1
@KevinMorrissey Myślę, że Coling mówił, że musisz umieścić „nie” przed „kontekstem”. ponieważ "return Not context.Employees.Any (c => c.PayrollNumber = PropertyToCheck)" NIE JEST (powtarzam), NIE JEST to samo, co "return context.Employees.Any (c <> c.PayrollNumber = PropertyToCheck)" . Czy rozumiesz, o co mi chodzi? Użycie „return Any <>” oznacza, że ​​jeśli znajdziesz coś, co nie pasuje do tej liczby (nawet jeśli istnieje pasująca liczba), zwróci wartość true bez względu na wszystko. Zamiast tego użycie „Not [...]. Any =” zwróci True tylko wtedy, gdy nie może znaleźć wiersza, którego szukasz! Czy widzisz różnicę?
Erx_VB.NExT.Coder
2

Miałem z tym problem - mój EntityKey składa się z trzech właściwości (PK z 3 kolumnami) i nie chciałem sprawdzać każdej z kolumn, ponieważ byłoby to brzydkie. Pomyślałem o rozwiązaniu, które działa cały czas ze wszystkimi podmiotami.

Innym powodem jest to, że nie lubię za każdym razem łapać UpdateExceptions.

Potrzebna jest odrobina refleksji, aby uzyskać wartości właściwości klucza.

Kod jest zaimplementowany jako rozszerzenie, aby uprościć użytkowanie, jako:

context.EntityExists<MyEntityType>(item);

Spójrz:

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
źródło
1
Chciałbym dodać komentarz do mojej odpowiedzi, która ma już prawie 9 lat. Myślę, że w dzisiejszych czasach są znacznie czystsze rozwiązania i możliwości niż w 2010/2011 z Entity Framwork 4. Dlatego poleciłbym przestać głosować na tę odpowiedź, ale zamiast tego dodać nową / lepszą odpowiedź poniżej.
Sven
Proszę również pamiętać, że moje rozwiązanie było ogólnym rozwiązaniem, które działało dla wielu jednostek z kluczami złożonymi istniejących tabel / jednostek, których nie mogłem zmienić. Więc zamiast zawsze sprawdzać .Any (...) z 3 kluczowymi właściwościami, po prostu wywołałem .EntityExists ().
Sven
2

Po prostu sprawdzam, czy obiekt jest pusty, u mnie działa w 100%

    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";
    }

źródło
0

Dlaczego tego nie zrobisz?

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

if(result?.field == value)
{
  // Match!
}
Matheus Miranda
źródło
Spowoduje to zgłoszenie zerowego wyjątku odwołania, ponieważ FirstOrDefault () zwróci wartość null, jeśli nie może znaleźć wyniku. Myślę, że możesz zrobić, jeśli (wynik? .Field == wartość), aby tego uniknąć.
ToDevAndBeyond
Może to być niepotrzebnie powolne, ponieważ ładuje jednostkę. Jeśli wszystko, co chcesz zrobić, to sprawdzić, czy klucz istnieje, czy nie.
Douglas Gaskell
0

Najlepszy sposób na zrobienie tego

Niezależnie od tego, jaki jest twój obiekt i dla jakiej tabeli w bazie danych, jedyne, co musisz mieć, to klucz podstawowy w obiekcie.

Kod C #

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

Kod VB.NET

Dim dbValue = EntityObject.Entry(obj).GetDatabaseValues()
If dbValue Is Nothing Then
   Don't exist
End If
Bazylia
źródło
Dlaczego dwie prawie identyczne odpowiedzi? Różnica jest nieznaczna. Z pewnością nie jest to najlepszy sposób na zrobienie tego. Nie ma sensu wyciąganie wszystkich wartości z bazy danych tylko w celu sprawdzenia, czy rekord istnieje .
Gert Arnold