Não é
while vs foreach isso faz a diferença. É a chamada para .Cast<T>() . Na primeira amostra, você está cedendo mesmo objeto em cada iteração do loop while. Se você não tomar cuidado, você acaba concluindo o iterador de rendimento antes de realmente usar os dados, e o DataReader já será descartado. Isso pode acontecer se você, digamos, chamar
.ToList() depois de chamar este método. O melhor que você poderia esperar seria que cada registro na lista tivesse o mesmo valor.(Dica profissional:na maioria das vezes você não quer chamar
.ToList() até que você absolutamente precise. É melhor trabalhar apenas com registros IEnumerable). No segundo exemplo, quando você chama
.Cast<T>() no leitor de dados, você está efetivamente fazendo uma cópia dos dados conforme ele percorre cada registro. Agora você não está mais produzindo o mesmo objeto.