2011-04-26 31 views
10

Recientemente migré este código a Entity Framework 4 y está fallando. Obviamente, si el estado no tiene un valor, devuelve todas las coincidencias, si tiene un valor que coincida con user.StatusID == 1.Consulta LINQ condicional simple en Entity Framework 4

return users.SingleOrDefault(
       user => 
       user.Username == username && 
       user.EncryptedPassword == password && 
       (!status.HasValue || user.StatusID == 1) 
       ); 

excepción devuelta:

ArgumentException: The specified value is not an instance of type 'Edm.Int32' 
Parameter name: value 

Sin embargo, la eliminación de la prueba condicional y funciona bien:

return users.SingleOrDefault(
       user => 
       user.Username == username && 
       user.EncryptedPassword == password && 
       user.StatusID == 1 
       ); 

¿Alguna idea? ¿Cómo se realizan las pruebas condicionales en EF 4? Seguramente no separar si las líneas?

Utilizo estas pruebas condicionales una y otra vez en Linq a Sql; es realmente extraño por qué esto no funciona en EF 4. Debe haber algo simple que va mal, tal vez haya una forma alternativa recomendada de hacer las cosas en EF 4.0.

Gracias por su ayuda chicos,
Graham

+0

what the 'Edm.Int32'? ¿Es su propia implementación de 'System.Int32'? – Stecya

+1

@Stecya: http://msdn.microsoft.com/en-us/library/bb387164.aspx, parece ser algunas partes internas de EF. – Euphoric

Respuesta

5

Ok, lo resolví por una combinación de dos cosas.

  1. Haciendo una simple prueba nula.
  2. Prueba de la conversión local status variable sin método .Value.

Ambos deben estar en su lugar, de lo contrario continuará fallando con el error. Hubiera sido bueno probar la propiedad de valor, pero creo que la consulta debe ser realmente simple, ¡bastante interesante!

return users.SingleOrDefault(
       user => 
       user.Username == username && 
       user.EncryptedPassword == password && 
       (status == null || user.StatusID == (int) status) 
       ); 

Voy a esperar a una mejor implementación de lo contrario acepto mi propia respuesta. Pero gracias por la ayuda de todos.

+0

EF se ve mejor y mejor ... Gracias por la solución, me ha mordido hoy. – greenoldman

0

lo que es status.HasValue? si el estado es un campo de User debe llamar así: user.status.HasValue

por lo que simplemente hacer esto:

if(status.HasValue) 
    return users.SingleOrDefault(
       user => 
       user.Username == username && 
       user.EncryptedPassword == password && 
       user.StatusID == 1 
       ); 

    return users.SingleOrDefault(
       user => 
       user.Username == username && 
       user.EncryptedPassword == password 
       ); 
+0

Hola Farzin, es un int nullable, y más bien un campo del método adjunto. – GONeale

+0

editado, sé que esta no es una solución común, pero ¡funciona para usted! ;) –

+0

Eek, mantenimiento de dos consultas. No, gracias, pero gracias de todos modos =) – GONeale

0

Creo que debería incluir user.StatusID == 1 en conjunto adicional de paréntesis, como yo pensar que actualmente EF intenta aplicar || operación a status.HasValue y user.StatusId y luego compare el resultado a 1.

+0

Ay, no hay suerte. Buena idea sin embargo. – GONeale

2

¿Qué le parece crear una variable separada para !status.HasValue y utilizar esta opción en la consulta?

Creo que el problema aquí es que EF intenta pasar su variable de estado como parámetro a la consulta y luego hacer la lógica en el SQL mismo. Intente verificar qué SQL se genera.

+0

Wow, eso pasa, declarando 'var test =! Status.HasValue' luego prueba de prueba. Pero no es bueno. Sin embargo, me ha llevado a probar todos los trucos de casting y bracketing que puedo pensar, porque seguramente si funciona así, puede funcionar en línea, ambos fallan. Tararear. ¿Alguna idea adicional? – GONeale

+0

Creo que el problema radica en el hecho de que 'status' no es un int; y se está confundiendo al comparar una enum y un tipo int en una construcción. Ningún casting puede funcionar porque es anulable :( – GONeale

2

Una forma alternativa de hacer la comprobación nula dentro de la cláusula where sería separar el parámetro opcional y aplicarlo solo si es necesario.

p. Ej.

var data = users.Where(
     user => 
      user.Username == username && 
      user.EncryptedPassword == password 
     ); 

    if (status.HasValue) 
    { 
     data = data.Where(user => user.StatusID == status.Value); 
    } 

    return data.FirstOrDefault(); 

no creo que va a ofrecer mucho más de la solución actual que no sea un poco más la legibilidad.

+0

Gracias por la idea – GONeale

0

En la primera galance que appeare como esto en mi mente:

return users.SingleOrDefault(
       user => 
       user.Username == username && 
       user.EncryptedPassword == password && 
       (status == null || user.StatusID == 1) 
       ); 

pero cuando lo miro más me siento que esta mal, pero ¿Qué pasa?

return users.SingleOrDefault(
    user => 
    user.Username == username && 
    user.EncryptedPassword == password && 
    status != null && 
    user.StatusID == 1) 
); 

¿cuál es exactamente la lógica de negocio detrás de él?

+0

Ah, bueno, su segunda consulta siempre hará cumplir el requisito de un estado, que no es necesariamente el caso. 'status' podría ser' null' y en ese caso, quiero buscar todas las entradas. Es por eso que uso el operador '||'. – GONeale

1

Parece que ya has encontrado una solución ...

Sin embargo, lo digo ... no tengo ningún problema con la siguiente línea en VS 2010

Nullable<int> status = 0; 
    String username = "Alexandre 2"; 

    var test = _context.Contacts.SingleOrDefault(c => c.FirstName == username && (!status.HasValue || c.ContactID == 1)); 

me sale ningún error y el objeto de contacto que esperaba es el retorno ... así que, en todo caso, me pregunto cuál es el tipo de su campo user.StatusID?

mejor de las suertes

+0

¿Esto es con EF 4? – GONeale

Cuestiones relacionadas