¿La mejor manera de verificar si el objeto existe en Entity Framework?

114

¿Cuál es la mejor forma de comprobar si un objeto existe en la base de datos desde el punto de vista del rendimiento? Estoy usando Entity Framework 1.0 (ASP.NET 3.5 SP1).

Freddy
fuente

Respuestas:

228

Si no desea ejecutar SQL directamente, la mejor manera es usar Any () . Esto se debe a que Any () regresará tan pronto como encuentre una coincidencia. Otra opción es Count () , pero es posible que deba verificar cada fila antes de regresar.

Aquí hay un ejemplo de cómo usarlo:

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

Y en vb.net

If context.MyEntity.Any(function(o) o.Id = idToMatch) Then
    ' Match!
End If
Alex Angas
fuente
Y en VB If (context.MyEntity.Any (o => o.Id <> idToMAtch)) Then '¡Esto es una coincidencia! Fin Si Lo siento, esto no está en la etiqueta de código, ¡no pude averiguar cómo hacerlo!
Kevin Morrissey
¿Crees que te refieres a o.Id <> idToMatch NO es igual a una coincidencia?
Colin
¿Qué pasa si busco por nombre y quiero obtener la identificación si existe?
Mihai Bratulescu
Hola. ¿Cómo podemos verificar si existe y luego seleccionar todos sus datos?
virtouso
1
@barnes Si se limita Ta una interfaz que es IEnumerabley devuelve objetos que contienen un Id, debería poder usar su función genérica IsExists<T>().
Suncat2000
5

Tuve que administrar un escenario en el que el porcentaje de duplicados que se proporcionaban en los nuevos registros de datos era muy alto y se estaban realizando miles de llamadas a la base de datos para verificar si había duplicados (por lo que la CPU envió mucho tiempo al 100%). Al final, decidí mantener los últimos 100.000 registros en caché en la memoria. De esta manera, pude verificar si hay duplicados en los registros en caché, lo cual fue extremadamente rápido en comparación con una consulta LINQ en la base de datos SQL, y luego escribir cualquier registro genuinamente nuevo en la base de datos (así como agregarlos a la caché de datos, que también ordenados y recortados para mantener su longitud manejable).

Tenga en cuenta que los datos sin procesar eran un archivo CSV que contenía muchos registros individuales que debían analizarse. Los registros en cada archivo consecutivo (que llegó a una velocidad de aproximadamente 1 cada 5 minutos) se superpusieron considerablemente, de ahí el alto porcentaje de duplicados.

En resumen, si ingresa datos sin procesar con marca de tiempo, prácticamente en orden, entonces el uso de una memoria caché podría ayudar con la verificación de duplicación de registros.

ProfNimrod
fuente
2
Muchas veces a los desarrolladores se les ocurre su escenario, puede ser con algunos giros. Me gustaría solicitarle que traduzca su solución en C # para que nosotros y muchos futuros desarrolladores nos beneficiemos. +1. ¡Me encantaría que la solución se expandiera a una publicación de blog! :)
sangam
3

Sé que este es un hilo muy antiguo, pero en caso de que alguien como yo necesite esta solución, pero en VB.NET esto es lo que usé en base a las respuestas anteriores.

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
fuente
No sé cómo usar Lambda en VB pero en C # esto es equivalente: return! Context.Employees.Any (c => c.PayrollNumber == PropertyToCheck). Esto evita devolver todos los resultados y luego recorrerlos en la memoria.
Colin
1
@ Colin esta es una buena adición Pasé por alto el problema de la memoria con el código anterior, en VB el código es context.Employees.Any (c => c.PayrollNumber <> PropertyToCheck). Ahora agregué esto a mi código.
Kevin Morrissey
Kevin, creo que puede que tengas que volver atrás y corregir tu código. Su lógica seguramente está volviendo verdadera si hay números de nómina que no coinciden, en lugar de verdadero cuando no hay números de nómina coincidentes.
Colin
@ Colin lo siento, tienes razón, estaba proporcionando una versión VB para tu ejemplo, solo que no corrijo mucho C # y pensé que == no era igual a, por lo tanto, mi VB <>.
Kevin Morrissey
1
@KevinMorrissey Creo que Coling estaba diciendo que necesitas poner un "No" delante del "contexto". ya que "return Not context.Employees.Any (c => c.PayrollNumber = PropertyToCheck)" NO ES (repito), NO ES lo mismo que "return context.Employees.Any (c <> c.PayrollNumber = PropertyToCheck)" . ¿Ves mi punto? Usar "return Any <>" significa que si encuentra alguno que no coincida con este número (incluso si existe uno coincidente), devolverá verdadero sin importar qué. En cambio, usar "Not [...]. Any =" solo devolverá True cuando no pueda encontrar la fila que está buscando. ¿Ves la diferencia?
Erx_VB.NExT.Coder
2

Tuve algunos problemas con esto: mi EntityKey consta de tres propiedades (PK con 3 columnas) y no quería verificar cada una de las columnas porque eso sería feo. Pensé en una solución que funcione todo el tiempo con todas las entidades.

Otra razón para esto es que no me gusta detectar UpdateExceptions cada vez.

Se necesita un poco de reflexión para obtener los valores de las propiedades clave.

El código se implementa como una extensión para simplificar el uso como:

context.EntityExists<MyEntityType>(item);

Echar un vistazo:

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
fuente
1
Me gustaría agregar un comentario a mi respuesta que ahora tiene casi 9 años. Creo que hoy en día hay soluciones y posibilidades mucho más limpias que en 2010/2011 con Entity Framwork 4. Por lo tanto, recomendaría dejar de votar en contra de esta respuesta y, en cambio, agregar una nueva / mejor respuesta a continuación.
Sven
También tenga en cuenta que mi solución era genérica y funcionaba para muchas entidades con claves compuestas de tablas / entidades existentes que no pude cambiar. Entonces, en lugar de consultar siempre .Any (...) con 3 propiedades clave, simplemente llamé .EntityExists ().
Sven
2

Solo verifico si el objeto es nulo, funciona al 100% para mí

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

fuente
0

¿Por qué no hacerlo?

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

if(result?.field == value)
{
  // Match!
}
Matheus Miranda
fuente
Esto arrojará una excepción de referencia nula porque FirstOrDefault () devolverá nulo si no puede encontrar un resultado. Supongo que podría hacer if (result? .Field == value) para evitar eso.
ToDevAndBeyond
Esto puede ser innecesariamente lento ya que carga la entidad. Si todo lo que quieres hacer es comprobar que existe una clave o no.
Douglas Gaskell
0

La mejor manera de hacerlo

Independientemente de cuál sea su objeto y para qué tabla en la base de datos, lo único que necesita es tener la clave principal en el 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
Albahaca
fuente
¿Por qué dos respuestas casi idénticas? La diferencia es insignificante. Además, esta ciertamente no es la mejor manera de hacerlo. No tiene sentido extraer todos los valores de la base de datos solo para verificar si existe un registro .
Gert Arnold