No se controló InvalidOperationException: Sequence contains no elements

Hace relativamente poco tiempo estaba trabajando con un Web Service bastante bien hasta que de la nada apareció este error que me dejó revisando documentación en internet de forma infructuosa (por no haber leído bien el mensaje de error debo reconocer) y descubrí que el error era tan simple como que evidentemente no estaba disponible el elemento que estaba intentando acceder.

Ok pero ¿Cómo es esto posible? Bien, los Servicios Web  no siempre devuelven los datos que esperamos, en mi caso, existe un dato que no está disponible para todos los conjuntos de datos por lo que cuando accedía a ese dato en particular obtenía el error.  Pongámolo de forma gráfica, supongamos que esperamos un XML como sigue:

<Persona>
<id>99999999</id>
<nombre>Nombre</nombre>
<apellido>Apellido</apellido>
<numhijos>2</numhijos>
</Persona>

Pero el campo número de hijos no está disponible porque  por alguna razón desconocida decidieron no enviarlo cuando es cero, es decir, en cuando una persona no tiene hijos recibimos un XML como el siguiente:

<Persona>
<id>99999999</id>
<nombre>Nombre</nombre>
<apellido>Apellido</apellido>
</Persona>

Y si nuestro código es algo como:


XDocument doc = XDocument.Parse(e.Result);
var query = (from person in doc.Descendants("Persona") select
new Persona()
{
id = person.Descendants("id").First().Value,
Nombre = person.Descendants("nombre").First().Value,
Apellido = person.Descendants("apellido").First().Value,
NumHijos = person.Descendants("numhijos").First().Value
}).Distinct();

Entonces obtendremos el error de arriba, para solucionar basta cambiar el código sólo un poco para deshacernos de este error. Por ejemplo:


XDocument doc = XDocument.Parse(e.Result);
var query = (from person in doc.Descendants("Persona") let hijos = person.Descendants("numhijos") select
new Persona()
{
id = person.Descendants("id").First().Value,
Nombre = person.Descendants("nombre").First().Value,
Apellido = person.Descendants("apellido").First().Value,
NumHijos = hijos == null ? 0 : hijos.Value
}).Distinct();

ACTUALIZACIÓN: Algunas personas me han preguntado si no hay una forma más «efectiva» de hacer esto sin necesidad de usar let y en efecto la hay, sería algo como:

NumHijos = person.Descendants("numhijos").Count() == 0 ? 0 : person.Descendants("numhijos").First().Value;

FIN_ACTUALIZACIÓN

Realmente se debería comprobar que todos los datos sean válidos pero con fines de demostración sólo lo hago para el campo que estoy seguro puede variar.

Otra cuestión que sirve de aporte, es que existen diversos Servicios Web que también nos proporcionan datos repetidos y queremos deshacernos de ellos, entonces utilizamos el método Distinct() de LINQ que nos permite filtrar esos datos indeseados.