Pergunta

Eu tenho esse código (ok, eu não sei, mas algo semelhante: p)

    var dogs = Dogs.Select(ø => new Row
    {
            Name = ø.Name,
            WeightOfNiceCats = ø.Owner
              .Cats
              .Where(æ => !æ.Annoying)
              .Sum(æ => æ.Weight),
    });

Aqui eu passar por todos os cães e soma-se o peso (em um decimal não-nulo) de todos os gatos não-irritantes que tem o mesmo proprietário do cão. Claro, praticamente todos os gatos são irritante, por isso eu recebo este erro:

O valor nulo não pode ser atribuído a um membro com o tipo System.Decimal que é um anulável não-tipo de valor.

Nenhum dos campos ou chaves estrangeiras utilizadas pode ser nulo. Assim, o erro acontece quando a cláusula Where não retorna nenhum gatos, que muitas vezes faz. Mas como posso resolver isso? Eu quero que ele retornar 0 quando isso acontece. Tentou com um DefaultIfEmpty() após a cláusula Where, mas depois eu recebo este erro:

objecto de referência não definida para uma instância de um objecto.

que eu acho que é compreensível. Eu tentei adicionar um ?? após a Sum, mas então ele não vai compilar devido a este erro:

Operador '??' não pode ser aplicado a operandos do tipo 'decimal' e 'decimal'

O que também faz sentido, é claro. Então o que eu posso fazer? Seria bom se a coisa Sum apenas retornou 0 quando não havia nada a soma. Ou uma declaração SumOrZero de algum tipo. Seria difícil fazer um método SumOrZero que trabalhou com Linq2SQL?

Foi útil?

Solução

Isto é o que eu acabei com agora:

.Sum(æ => (decimal?) æ.Weight) ?? 0.0M,

Isso funciona como um encanto.

Eu ainda preferiria ter um SumOrZero eu poderia usar, que iria funcionar exatamente como o Sum regular, exceto que ele nunca mais voltaria null por qualquer motivo. Como os outros notaram, isso seria muito fácil de fazer para IEnumerable mas um pouco mais icky criar para IQueryable para que ele iria trabalhar com Linq2SQL, etc. Então eu só vou deixar por isso mesmo, por enquanto. Embora se alguém está entediado um dia e não escrever uma SumOrZero para IQueryable que trabalha com Linq2SQL para todos os tipos numéricos, por favor, deixe-me saber: D

Outras dicas

Em LINQ to Objects seria fácil. Com LINQ to SQL que será mais difícil. Você pode escrever seu próprio método de extensão que chamou Queryable.Sum com uma expressão construída a partir do normal, com um elenco com o tipo anulável. Eu suspeito que você precisa para fazer o ?? 0m no entanto código de chamada. Em outras palavras, você poderia fazer:

.SumOrNull(æ => æ.Weight) ?? 0M,

Onde a assinatura para .SumOrNull seria:

public static decimal? SumOrNull<TSource, decimal>(this IQueryable<TSource>,
    Func<TSource,decimal> projection)

Você pode basicamente escrever que, para todos os tipos de valores suportados no Queryable. Você poderia escrevê-lo genericamente, e chamar o método apropriado na Queryable usando a reflexão, mas isso seria nojento também.

Eu acho que você é melhor fora com o que você tem, para ser honesto.

O truque que você já tenha dado (.Sum(æ => (decimal?) æ.Weight) ?? 0.0M) é sobre o melhor que eu posso pensar para este cenário quando ele executa como uma consulta ao banco de dados. Um pouco chato, mas existem coisas piores ...

Ao contrário de LINQ-to-Objects, você não pode simplesmente adicionar seu próprio método de extensão, já que precisa ser mapeado pelo provedor subjacente.

Você quer usar DefaultIfEmpty, que retornará um IEnumberable com um único elemento de 0, e que vai ser semanticamente o mesmo que você precisar.

Como você tem um decimal anulável, você provavelmente terá que usar a segunda versão do método de tomar 2 parâmetros.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top