Лучший способ проверить, существует ли объект в Entity Framework?

114

Как лучше всего проверить, существует ли объект в базе данных с точки зрения производительности? Я использую Entity Framework 1.0 (ASP.NET 3.5 SP1).

Фредди
источник

Ответы:

228

Если вы не хотите выполнять SQL напрямую, лучше всего использовать Any () . Это потому, что Any () вернется, как только найдет совпадение. Другой вариант - Count () , но перед возвратом может потребоваться проверка каждой строки.

Вот пример того, как его использовать:

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

И в vb.net

If context.MyEntity.Any(function(o) o.Id = idToMatch) Then
    ' Match!
End If
Алекс Ангас
источник
И в VB If (context.MyEntity.Any (o => o.Id <> idToMAtch)) Then 'Это совпадение! Конец Если извините, этого нет в теге кода, я не мог понять, как это сделать!
Кевин Моррисси
Думаю, вы имеете в виду, что o.Id <> idToMatch НЕ равно совпадению
Колин
что, если я ищу по имени и хочу получить идентификатор, если он существует?
Михай Братулеску
Здравствуй. как мы можем проверить, существует ли он, и после этого выбрать все его данные?
virtouso 07
1
@barnes Если вы ограничиваетесь Tинтерфейсом, который есть, IEnumerableи возвращаете объекты, содержащие Id, вы сможете использовать свою универсальную функцию IsExists<T>().
Suncat2000
9

С точки зрения производительности я полагаю, что прямой SQL-запрос с использованием команды EXISTS был бы уместен. См. Здесь, как выполнить SQL непосредственно в Entity Framework: http://blogs.microsoft.co.il/blogs/gilf/archive/2009/11/25/execute-t-sql-statements-in-entity-framework- 4. aspx

Konamiman
источник
Да, хорошая идея, но я ограничен предыдущей версией entity framework.
Фредди
5

Мне пришлось управлять сценарием, в котором процент дубликатов, предоставляемых в новых записях данных, был очень высок, и для проверки дубликатов выполнялось очень много тысяч вызовов базы данных (так что ЦП отправлял много времени на 100%). В конце концов, я решил оставить последние 100 000 записей в кеш-памяти. Таким образом, я мог проверять наличие дубликатов кэшированных записей, что было чрезвычайно быстро по сравнению с запросом LINQ к базе данных SQL, а затем записывать любые действительно новые записи в базу данных (а также добавлять их в кеш данных, что я также отсортированы и обрезаны, чтобы длина оставалась управляемой).

Обратите внимание, что исходные данные были файлом CSV, содержащим множество отдельных записей, которые необходимо было проанализировать. Записи в каждом последующем файле (который поступает примерно 1 раз каждые 5 минут) значительно перекрываются, отсюда высокий процент дубликатов.

Короче говоря, если у вас есть входящие необработанные данные с метками времени, в основном в порядке, то использование кеша памяти может помочь при проверке дублирования записей.

ПрофНимрод
источник
2
Часто мы, разработчики, придумываем ваш сценарий, возможно, с некоторыми особенностями. Я хотел бы попросить вас перевести ваше решение на C #, чтобы мы и многие будущие разработчики получили пользу. +1. Мне тоже хотелось бы, чтобы решение было расширено до сообщения в блоге! :)
sangam
3

Я знаю, что это очень старый поток, но просто на случай, если кому-то вроде меня понадобится это решение, но в VB.NET вот что я использовал на основе ответов выше.

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
Кевин Моррисси
источник
Я не знаю, как использовать Lambda в VB, но в C # это эквивалентно: return! Context.Employees.Any (c => c.PayrollNumber == PropertyToCheck). Это позволяет избежать возврата всех результатов в цикле в памяти.
Colin
1
@Colin, это хорошее дополнение. Я упустил из виду проблему с памятью в приведенном выше коде, в VB код - context.Employees.Any (c => c.PayrollNumber <> PropertyToCheck). Я добавил это в свой код.
Кевин Моррисси
Кевин, я думаю, тебе, возможно, придется вернуться и исправить свой код. Ваша логика наверняка возвращает истину, если есть какие-либо номера зарплат, которые не совпадают, а не истину, когда нет никаких совпадающих номеров зарплат.
Colin
@Colin извините, что вы правы, я предоставил версию VB для вашего примера, только я не очень прав C # и подумал, что == не равно, следовательно, моему VB <>.
Кевин Моррисси,
1
@KevinMorrissey Я думаю, Колинг говорил, что вам нужно поставить «Не» перед словом «контекст». так как "возвращение не context.Employees.Any (с => c.PayrollNumber = PropertyToCheck)" IS NOT (я повторюсь), IS NOT такой же , как "возвращение context.Employees.Any (с <> c.PayrollNumber = PropertyToCheck)" . Вы понимаете мою точку зрения? Использование «return Any <>» означает, что если вы найдете что-то, что не соответствует этому номеру (даже если оно существует), вернет true, несмотря ни на что. Вместо этого использование Not [...]. Any = вернет True только тогда, когда не сможет найти строку, которую вы ищете! Вы видите разницу?
Erx_VB.NExT.Coder 02 окт.13,
2

У меня были проблемы с этим - мой EntityKey состоит из трех свойств (PK с 3 столбцами), и я не хотел проверять каждый из столбцов, потому что это было бы некрасиво. Я думал о решении, которое работает постоянно со всеми сущностями.

Другая причина в том, что я не люблю каждый раз ловить UpdateExceptions.

Для получения значений ключевых свойств требуется немного Reflection.

Код реализован как расширение для упрощения использования:

context.EntityExists<MyEntityType>(item);

Посмотри:

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;
    }
Свен
источник
1
Хочу добавить комментарий к своему ответу, которому уже почти 9 лет. Я думаю, что в настоящее время есть гораздо более чистые решения и возможности, чем в 2010/2011 годах с Entity Framwork 4. Поэтому я бы рекомендовал прекратить отрицательное голосование за этот ответ, а вместо этого добавить новый / лучший ответ ниже.
Sven
Также имейте в виду, что мое решение было общим, которое работало для многих объектов с составными ключами существующих таблиц / объектов, которые я не мог изменить. Поэтому вместо того, чтобы всегда запрашивать .Any (...) с тремя ключевыми свойствами, я просто вызвал .EntityExists ().
Sven
2

Я просто проверяю, является ли объект нулевым, у меня он работает на 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";
    }

источник
0

Почему бы не сделать это?

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

if(result?.field == value)
{
  // Match!
}
Матеус Миранда
источник
Это вызовет исключение нулевой ссылки, потому что FirstOrDefault () вернет null, если не сможет найти результат. Я думаю, вы могли бы сделать if (result? .Field == value), чтобы этого избежать.
ToDevAndBeyond
Это может быть излишне медленным, поскольку загружает объект. Если все, что вам нужно, это проверить, существует ли ключ.
Дуглас Гаскелл
0

Лучший способ сделать это

Независимо от того, что это за объект и для какой таблицы в базе данных, единственное, что вам нужно, - это первичный ключ в объекте.

Код C #

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

Код VB.NET

Dim dbValue = EntityObject.Entry(obj).GetDatabaseValues()
If dbValue Is Nothing Then
   Don't exist
End If
Бэзил
источник
Почему два почти одинаковых ответа? Разница незначительная. Кроме того, это определенно не лучший способ сделать это. Это не имеет смысла тянуть аль ценности формируют базу данных только для проверки , если запись существует .
Герт Арнольд,