A sequência não contém nenhum elemento correspondente

112

Eu tenho um aplicativo asp.net no qual estou usando o linq para manipulação de dados. Durante a execução, recebo a exceção "Sequência não contém elemento correspondente".

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
fonte

Respostas:

220

Bem, espero que seja esta linha que está lançando a exceção:

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

First()lançará uma exceção se não puder encontrar nenhum elemento correspondente. Dado que você está testando para null imediatamente depois, parece que você deseja FirstOrDefault(), o que retorna o valor padrão para o tipo de elemento (que é nulo para tipos de referência) se nenhum item correspondente for encontrado:

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

Outras opções a serem consideradas em algumas situações são Single()(quando você acredita que há exatamente um elemento correspondente) e SingleOrDefault()(quando você acredita que há exatamente um ou zero elementos correspondentes). Suspeito que FirstOrDefaultseja a melhor opção neste caso em particular, mas vale a pena conhecer os outros de qualquer maneira.

Por outro lado, parece que você pode realmente se sair melhor com um join aqui em primeiro lugar. Se você não se importasse que faria todas as correspondências (em vez de apenas a primeira), você poderia 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
}

Isso é IMO mais simples e mais eficiente.

Mesmo se você não decidir manter o loop, eu tenho um par de sugestões:

  • Livre-se do exterior if. Você não precisa disso, como se Count for zero, o corpo do loop for nunca será executado
  • Use limites superiores exclusivos em loops for - eles são mais idiomáticos em C #:

    for (i = 0; i < _lstAcl.Documents.Count; i++)
  • Elimine subexpressões comuns:

    var target = _lstAcl.Documents[i];
    // Now use target for the rest of the loop body
  • Sempre que possível, use em foreachvez de forcomeçar com:

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

Use FirstOrDefault . Primeiro nunca retornará nulo - se não conseguir encontrar um elemento correspondente, ele lançará a exceção que você está vendo.

_dsACL.Documents.FirstOrDefault(o => o.ID == id);
Jakub Konecki
fonte
19
Apenas para esclarecer um pouco - primeiro pode retornar nulo em geral, se seu predicado corresponder a valores nulos. Ele simplesmente não pode retornar nulo aqui, pois o.IDlançaria uma NullReferenceException em um valor nulo.
Jon Skeet,
11

Da biblioteca MSDN:

O First<TSource>(IEnumerable<TSource>)método lança uma exceção se source não contém elementos. Em vez disso, para retornar um valor padrão quando a sequência de origem estiver vazia, use o FirstOrDefaultmétodo.

KBoek
fonte
0

Para aqueles que enfrentaram esse problema ao criar um controlador por meio do menu de contexto, a reabertura do Visual Studio como administrador corrigiu o problema.

Cinza
fonte
-4

Talvez usar Where () antes de First () possa ajudá-lo, já que meu problema foi resolvido neste caso.

var documentRow = _dsACL.Documents.Where(o => o.ID == id).FirstOrDefault();
Elnaz
fonte
3
O que realmente ajudou você aqui foi usar .FirstOrDefault () em vez de .First () - usando .Where (o => o.ID == id) .FirstOrDefault () e .FirstOrDefault (o => o.ID == id ) serão idênticos.
pwdst
@pwdst usando a condição na cláusula Where e, em seguida, FirstOrDefault sem qualquer expressão lambda.
Elnaz