Como é que a execução adiar LINQ, quando em uma instrução using
-
19-08-2019 - |
Pergunta
Imagine que eu tenha o seguinte:
private IEnumerable MyFunc(parameter a)
{
using(MyDataContext dc = new MyDataContext)
{
return dc.tablename.Select(row => row.parameter == a);
}
}
private void UsingFunc()
{
var result = MyFunc(new a());
foreach(var row in result)
{
//Do something
}
}
De acordo com a documentação da execução LINQ vai adiar até que eu enumerar real o resultado, que ocorre na fila do foreach. No entanto, a instrução using deve forçar o objeto a ser recolhidos de forma fiável no final da chamada para MyFunct ().
O que realmente acontece, quando é que o triturador de execução e / ou o resultado prazo?
A única coisa que posso pensar é a execução diferido é calculado em tempo de compilação, então a chamada real é movido pelo compilador para a primeira linha do foreach, fazendo com que o usando para executar corretamente, mas não executar até que a linha foreach ? Existe um guru lá fora que pode ajudar?
EDIT: NOTA: Este código não trabalho, eu só não entendo como
Eu fiz alguma leitura e percebi no meu código que eu tinha chamado o método de extensão ToList () que, naturalmente, enumera o resultado. O comportamento da resposta assinalada é perfeitamente correta para a questão real respondeu.
Pedimos desculpas por qualquer confusão.
Solução
Eu esperaria que simplesmente não trabalho; o Select
é diferido, de modo nenhum dado foi consumido neste momento. No entanto, desde que você tenha eliminado o contexto de dados (antes de sair MyFunc
), nunca será capaz de obter dados. A melhor opção é passar os dados ao contexto em o método, para que o consumidor pode escolher a vida. Além disso, eu recomendaria o retorno IQueryable<T>
para que o consumidor pode "compor" o resultado (ou seja, adicionar OrderBy
/ Skip
/ Take
/ Where
etc, e tê-lo impactar a consulta final):
// this could also be an instance method on the data-context
internal static IQueryable<SomeType> MyFunc(
this MyDataContext dc, parameter a)
{
return dc.tablename.Where(row => row.parameter == a);
}
private void UsingFunc()
{
using(MyDataContext dc = new MyDataContext()) {
var result = dc.MyFunc(new a());
foreach(var row in result)
{
//Do something
}
}
}
Atualização: se você (comentários) não querem execução adiar (ou seja, você não quer que o tráfico de chamador com o contexto de dados), então você precisa para avaliar os resultados. Você pode fazer isso chamando .ToList()
ou .ToArray()
sobre o resultado para o buffer os valores.
private IEnumerable<SomeType> MyFunc(parameter a)
{
using(MyDataContext dc = new MyDataContext)
{
// or ToList() etc
return dc.tablename.Where(row => row.parameter == a).ToArray();
}
}
Se você quiser mantê-lo adiado neste caso, então você precisa usar um "bloco de iterator":
private IEnumerable<SomeType> MyFunc(parameter a)
{
using(MyDataContext dc = new MyDataContext)
{
foreach(SomeType row in dc
.tablename.Where(row => row.parameter == a))
{
yield return row;
}
}
}
Esta é agora adiada sem passar o contexto de dados ao redor.
Outras dicas
Acabei de publicar uma outra solução execução adiada para este problema aqui , incluindo este código de exemplo:
IQueryable<MyType> MyFunc(string myValue)
{
return from dc in new MyDataContext().Use()
from row in dc.MyTable
where row.MyField == myValue
select row;
}
void UsingFunc()
{
var result = MyFunc("MyValue").OrderBy(row => row.SortOrder);
foreach(var row in result)
{
//Do something
}
}
O método de extensão Use()
age essencialmente como um bloco using
adiada.