Melhor maneira de verificar se o objeto existe no Entity Framework?

114

Qual é a melhor maneira de verificar se um objeto existe no banco de dados do ponto de vista de desempenho? Estou usando o Entity Framework 1.0 (ASP.NET 3.5 SP1).

Freddy
fonte

Respostas:

228

Se você não deseja executar SQL diretamente, a melhor maneira é usar Any () . Isso ocorre porque Any () retornará assim que encontrar uma correspondência. Outra opção é Count () , mas pode ser necessário verificar todas as linhas antes de retornar.

Aqui está um exemplo de como usá-lo:

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

E em vb.net

If context.MyEntity.Any(function(o) o.Id = idToMatch) Then
    ' Match!
End If
Alex Angas
fonte
E em VB If (context.MyEntity.Any (o => o.Id <> idToMAtch)) Then 'Esta é uma correspondência! End If Sorry, isto não está na tag de código, eu não consegui descobrir como fazer isso!
Kevin Morrissey
Pense que você quer dizer o.Id <> idToMatch NÃO é igual a uma correspondência
Colin
e se eu pesquisar por nome e quiser obter o ID, se ele existir?
Mihai Bratulescu
Oi. como podemos verificar se ele existe e depois selecionar todos os seus dados?
virtouso
1
@barnes Se você restringir Ta uma interface que é IEnumerablee retornar objetos que contêm um Id, você deve ser capaz de usar sua função genérica IsExists<T>().
Suncat2000
5

Tive que gerenciar um cenário em que a porcentagem de duplicatas fornecidas nos novos registros de dados era muito alta, e tantos milhares de chamadas de banco de dados estavam sendo feitas para verificar a existência de duplicatas (portanto, a CPU enviava muito tempo a 100%). No final, decidi manter os últimos 100.000 registros em cache na memória. Dessa forma, eu poderia verificar se há duplicatas nos registros armazenados em cache, o que era extremamente rápido quando comparado a uma consulta LINQ no banco de dados SQL e, em seguida, gravar quaisquer registros genuinamente novos no banco de dados (bem como adicioná-los ao cache de dados, o que também classificados e aparados para manter seu comprimento gerenciável).

Observe que os dados brutos eram um arquivo CSV que continha muitos registros individuais que precisavam ser analisados. Os registros em cada arquivo consecutivo (que chegavam a uma taxa de cerca de 1 a cada 5 minutos) se sobrepunham consideravelmente, daí a alta porcentagem de duplicatas.

Em suma, se você tiver dados brutos com carimbo de data / hora chegando, praticamente em ordem, usar um cache de memória pode ajudar na verificação de duplicação de registros.

ProfNimrod
fonte
2
Muitas vezes, nós, desenvolvedores, criamos seu cenário, pode ser com algumas reviravoltas. Eu gostaria de solicitar que você traduza sua solução em C # para que nós e muitos desenvolvedores futuros nos beneficiemos. +1. Eu adoraria que a solução expandisse para uma postagem de blog também! :)
sangam
3

Eu sei que este é um tópico muito antigo, mas apenas no caso de alguém como eu precisa desta solução, mas em VB.NET aqui está o que usei com base nas respostas acima.

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
fonte
Não sei como usar Lambda em VB, mas em C # isso é equivalente: return! Context.Employees.Any (c => c.PayrollNumber == PropertyToCheck). Isso evita o retorno de todos os resultados e depois o loop na memória.
Colin
1
@Colin este é um bom acréscimo. Esqueci o problema de memória com o código acima, em VB o código é context.Employees.Any (c => c.PayrollNumber <> PropertyToCheck). Agora adicionei isso ao meu código.
Kevin Morrissey
Kevin, acho que você precisa voltar e corrigir seu código. Sua lógica certamente retornará verdadeiro se houver algum número da folha de pagamento que não corresponda, em vez de verdadeiro quando não houver nenhum número correspondente da folha de pagamento.
Colin
@Colin desculpe você está certo, eu estava fornecendo uma versão VB para o seu exemplo apenas não acertei muito C # e pensei que == não era igual a, portanto, meu VB <>.
Kevin Morrissey
1
@KevinMorrissey Acho que Coling estava dizendo que você precisa colocar um "Não" antes do "contexto". uma vez que "return Not context.Employees.Any (c => c.PayrollNumber = PropertyToCheck)" NÃO É (repito), NÃO É o mesmo que "return context.Employees.Any (c <> c.PayrollNumber = PropertyToCheck)" . Você entende meu ponto? Usar "return Any <>" significa que se você encontrar algum que não corresponda a este número (mesmo que exista um correspondente), retornará verdadeiro de qualquer maneira. Em vez disso, usar "Not [...]. Any =" só retornará True quando não puder encontrar a linha que você está procurando! Você vê a diferença?
Erx_VB.NExT.Coder
2

Eu tive alguns problemas com isso - minha EntityKey consiste em três propriedades (PK com 3 colunas) e eu não queria verificar cada uma das colunas porque isso seria feio. Pensei em uma solução que funcionasse o tempo todo com todas as entidades.

Outra razão para isso é que eu não gosto de capturar UpdateExceptions todas as vezes.

Um pouco de reflexão é necessário para obter os valores das propriedades-chave.

O código é implementado como uma extensão para simplificar o uso como:

context.EntityExists<MyEntityType>(item);

Dar uma olhada:

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
fonte
1
Eu gostaria de adicionar um comentário à minha resposta que agora tem quase 9 anos. Acho que hoje em dia existem soluções e possibilidades muito mais limpas do que havia em 2010/2011 com o Entity Framwork 4. Então, eu recomendo parar de votar nesta resposta, mas em vez disso, adicione uma resposta nova / melhor abaixo.
Sven
Lembre-se também de que minha solução foi genérica e funcionou para muitas entidades com chaves compostas de tabelas / entidades existentes que não pude alterar. Portanto, em vez de sempre consultar. Qualquer (...) com 3 propriedades-chave, simplesmente chamei .EntityExists ().
Sven
2

Acabei de verificar se o objeto é nulo, funciona 100% para mim

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

fonte
0

Por que não fazer?

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

if(result?.field == value)
{
  // Match!
}
Matheus Miranda
fonte
Isso lançará uma exceção de referência nula porque FirstOrDefault () retornará nulo se não puder encontrar um resultado. Eu acho que você poderia fazer if (result? .Field == value) para evitar isso.
ToDevAndBeyond
Isso pode ser desnecessariamente lento, pois carrega a entidade. Se tudo o que você deseja fazer é verificar se existe uma chave ou não.
Douglas Gaskell
0

Melhor maneira de fazer isso

Independentemente de qual seja o seu objeto e de qual tabela no banco de dados, a única coisa que você precisa é a chave primária no objeto.

Código C #

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

Código VB.NET

Dim dbValue = EntityObject.Entry(obj).GetDatabaseValues()
If dbValue Is Nothing Then
   Don't exist
End If
Manjericão
fonte
Por que duas respostas quase idênticas? A diferença é insignificante. Além disso, essa certamente não é a melhor maneira de fazer isso. Não faz sentido extrair todos os valores do banco de dados apenas para verificar se existe um registro .
Gert Arnold