2010-07-06 23 views

Respuesta

25

Single y SingleOrDefault están diseñados para arrojar si existe más de una coincidencia en la secuencia. Una consecuencia de esto es que toda la secuencia debe repetirse antes de completarse. No parece que esto es lo que quieres. Trate FirstOrDefault lugar:

Feature f = o.Features 
    .FirstOrDefault(e => e.vcr_LinkName == PageLink && e.bit_Activate == true); 

Esto hará (en general) obtienen mejores resultados porque completa tan pronto como se encuentra una coincidencia.

Por supuesto, si usted realmente desea conservar más de un elemento, una cláusula Where sería más apropiado:

IEnumerable<Feature> fs = o.Features 
    .Where(e => e.vcr_LinkName == PageLink && e.bit_Activate == true); 
11

Si sólo desea que la primera elemento, utilice FirstOrDefault lugar.

Básicamente, aquí están las opciones en términos de resultados válidos (es decir, en el que no quiere tirar) y lo utilizan para:

  • Exactamente un: Single
  • uno o cero: SingleOrDefault
  • uno o más: First
  • cero o más: FirstOrDefault

(ElementAt y ElementAtOrDefault, Last y LastOrDefault están también disponibles.)

2

Single significa que se espera sea un elemento de la secuencia. SingleOrDefault significa que espera que haya uno o cero elementos en la secuencia. Esto debe usarse cuando desee saber que hay uno (o cero) y desea que se bloquee cuando se devuelve más de uno.

Si busca una sola, use First (o FirstOrDefault) como se sugiere arriba, pero asegúrese de ordenar los datos correctamente.

3

SingleOrDefault sugiere que espera entre 0 o 1 resultados. Si tiene más de 1, entonces hay algo mal con sus datos o consultas.

Si está esperando más de 1 resultado y solo desea el primero, entonces se debe usar FirstOrDefault.

14

alternativa, si sólo desea que el elemento cuando hay exactamente un partido y no quiere tirar cuando hay más de uno, esto se puede lograr fácilmente.He creado un método de extensión para este proyecto en mi:

public static class QueryableExtensions 
{ 
    public static TSource SingleWhenOnly<TSource>(this IQueryable<TSource> source) 
    { 
     if (source == null) 
      throw new ArgumentNullException("source"); 

     var results = source.Take(2).ToArray(); 

     return results.Length == 1 ? results[0] : default(TSource); 
    } 

    public static TSource SingleWhenOnly<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate) 
    { 
     if (source == null) 
      throw new ArgumentNullException("source"); 
     if (predicate == null) 
      throw new ArgumentNullException("predicate"); 

     var results = source.Where(predicate).Take(2).ToArray(); 

     return results.Length == 1 ? results[0] : default(TSource); 
    } 
} 
+1

Gracias por esto, estoy usando esto actualmente. Cambié el nombre a 'ExclusiveOrDefault' y también hice un método de extensión' Exclusive' que arroja un error cuando hay * elementos * cero *, pero devuelve nulo cuando hay * 2 o más * elementos. –

3

que he encontrado que necesito el comportamiento de devolver un valor por defecto si no hay exactamente un elemento (es decir, cero, dos, o más) más a menudo de lo que necesito el SingleOrDefault comportamiento normal, así que aquí está mi versión adaptada de Pieter van Ginkel's answer:

public static class LinqExtensions 
{ 
    /// <summary> 
    /// Returns the only element of a sequence, or a default value if the sequence is empty or contains more than one element. 
    /// </summary> 
    public static TSource SingleOrDefaultIfMultiple<TSource>(this IEnumerable<TSource> source) 
    { 
     var elements = source.Take(2).ToArray(); 

     return (elements.Length == 1) ? elements[0] : default(TSource); 
    } 

    /// <summary> 
    /// Returns the only element of a sequence, or a default value if the sequence is empty or contains more than one element. 
    /// </summary> 
    public static TSource SingleOrDefaultIfMultiple<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 
    { 
     return source.Where(predicate).SingleOrDefaultIfMultiple(); 
    } 

    /// <summary> 
    /// Returns the only element of a sequence, or a default value if the sequence is empty or contains more than one element. 
    /// </summary> 
    public static TSource SingleOrDefaultIfMultiple<TSource>(this IQueryable<TSource> source) 
    { 
     var elements = source.Take(2).ToArray(); 

     return (elements.Length == 1) ? elements[0] : default(TSource); 
    } 

    /// <summary> 
    /// Returns the only element of a sequence, or a default value if the sequence is empty or contains more than one element. 
    /// </summary> 
    public static TSource SingleOrDefaultIfMultiple<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate) 
    { 
     return source.Where(predicate).SingleOrDefaultIfMultiple(); 
    } 
} 

he omitido los controles argumentos son nulos porque yo estoy bien con depender de los Take y Where llamadas a lanzar excepciones cuando las los argumentos son nulos, pero puede que pienses lo contrario.

+0

Agradable. Sin embargo, he cambiado el nombre del extenso SingleOrDefaultIfMultiple() a OnlyOrDefault(). Sus comentarios incluso sugieren que ... – Marcel

+0

O, ¿qué pasa con "TheOneOrDefault" (Recientemente volví a ver la trilogía "The Matrix") jeje ... – Marcel

+1

@Marcel Me gusta 'OnlyOrDefault()', pero parte de mi razón porque el nombre más largo es para que surja como una opción intellisense cuando yo (u otros miembros del equipo) empiecen a escribir '.SingleOrDefault' y con suerte terminaremos escogiendo el que sea apropiado para la situación. –

Cuestiones relacionadas