La sequencia no contiene elementos coincidentes

112

Tengo una aplicación asp.net en la que estoy usando linq para la manipulación de datos. Mientras se ejecuta, obtengo la excepción "La secuencia no contiene ningún elemento coincidente".

if (_lstAcl.Documents.Count > 0)
{
    for (i = 0; i <= _lstAcl.Documents.Count - 1; i++)
    {
        string id = _lstAcl.Documents[i].ID.ToString();                           
        var documentRow = _dsACL.Documents.First(o => o.ID == id);
        if (documentRow !=null)
        {

            _lstAcl.Documents[i].Read = documentRow.Read;
            _lstAcl.Documents[i].ReadRule = documentRow.ReadRule;

            _lstAcl.Documents[i].Create= documentRow.Create;
            _lstAcl.Documents[i].CreateRule = documentRow.CreateRule;

            _lstAcl.Documents[i].Update = documentRow.Update;
            _lstAcl.Documents[i].UpdateRule = documentRow.UpdateRule;

            _lstAcl.Documents[i].Delete = documentRow.Delete;
            _lstAcl.Documents[i].DeleteRule = documentRow.DeleteRule;
        }
    }
}
MAC
fuente

Respuestas:

220

Bueno, esperaría que sea esta línea la que arroje la excepción:

var documentRow = _dsACL.Documents.First(o => o.ID == id)

First()lanzará una excepción si no puede encontrar ningún elemento coincidente. Dado que está probando nulo inmediatamente después, parece que lo desea FirstOrDefault(), lo que devuelve el valor predeterminado para el tipo de elemento (que es nulo para los tipos de referencia) si no se encuentran elementos coincidentes:

var documentRow = _dsACL.Documents.FirstOrDefault(o => o.ID == id)

Otras opciones a considerar en algunas situaciones son Single()(cuando cree que hay exactamente un elemento coincidente) y SingleOrDefault()(cuando cree que hay exactamente uno o cero elementos coincidentes). Sospecho que FirstOrDefaultes la mejor opción en este caso particular, pero vale la pena conocer las demás de todos modos.

Por otro lado, parece que, en primer lugar, podría estar mejor con una combinación aquí. Si no le importaba que hiciera todas las coincidencias (en lugar de solo la primera), puede usar:

var query = from target in _lstAcl.Documents
            join source in _dsAcl.Document
            where source.ID.ToString() equals target.ID
            select new { source, target };
foreach (var pair in query)
{
    target.Read = source.Read;
    target.ReadRule = source.ReadRule;
    // etc
}

Eso es IMO más simple y más eficiente.

Incluso si usted no decide mantener el bucle, tengo un par de sugerencias:

  • Deshazte del exterior if. No lo necesita, como si Count fuera cero, el cuerpo del bucle for nunca se ejecutará
  • Use límites superiores exclusivos en bucles for; son más idiomáticos en C #:

    for (i = 0; i < _lstAcl.Documents.Count; i++)
  • Eliminar subexpresiones comunes:

    var target = _lstAcl.Documents[i];
    // Now use target for the rest of the loop body
  • Cuando sea posible, utilice en foreachlugar de forcomenzar con:

    foreach (var target in _lstAcl.Documents)
Jon Skeet
fuente
39

Utilice FirstOrDefault . Primero nunca devolverá un valor nulo; si no puede encontrar un elemento coincidente, arroja la excepción que está viendo.

_dsACL.Documents.FirstOrDefault(o => o.ID == id);
Jakub Konecki
fuente
19
Solo para aclarar un poco: primero podría devolver un valor nulo en general, si su predicado coincide con valores nulos. Simplemente no puede devolver un valor nulo aquí, ya o.IDque arrojaría una excepción NullReferenceException en un valor nulo.
Jon Skeet
11

Desde la biblioteca de MSDN:

El First<TSource>(IEnumerable<TSource>)método lanza una excepción si la fuente no contiene elementos. En su lugar, para devolver un valor predeterminado cuando la secuencia de origen está vacía, use el FirstOrDefaultmétodo.

KBoek
fuente
0

Para aquellos de ustedes que enfrentaron este problema al crear un controlador a través del menú contextual, reabrir Visual Studio como administrador lo solucionó.

Ceniza
fuente
-4

Tal vez usar Where () antes de First () pueda ayudarlo, ya que mi problema se ha resuelto en este caso.

var documentRow = _dsACL.Documents.Where(o => o.ID == id).FirstOrDefault();
Elnaz
fuente
3
Lo que realmente te ha ayudado aquí es usar .FirstOrDefault () en lugar de .First () - usando .Where (o => o.ID == id) .FirstOrDefault () y .FirstOrDefault (o => o.ID == id ) será idéntico.
pwdst
@pwdst usando la condición en la cláusula Where y luego FirstOrDefault sin ninguna expresión lambda.
Elnaz