Pergunta

Eu estou escrevendo algo que carrega registros do SQL server para uma azure fila.A coisa é, o número de itens na caixa selecione resultado pode ser muito grande, então eu gostaria de iniciar o serviço de enfileiramento de coisas, enquanto os dados ainda estão sendo recuperados.

Eu estou tentando aproveitar EF6 (assíncrona), todos os métodos assíncronos e e TPL paralelamente, enqueueing.Então, eu tenho:

        // This defines queue that Generator will publsh to and 
        // QueueManager wil read from. More info:
        // http://msdn.microsoft.com/en-us/library/hh228601(v=vs.110).aspx
        var queue = new BufferBlock<ProcessQueueItem>();

        // Configure queue listener first
        var result = this.ReceiveAndEnqueue(queue);

        // Start generation process
        var tasks = generator.Generate(batchId);

O ReceiveAndEnqueue é simples:

    private async Task ReceiveAndEnqueue(ISourceBlock<ProcessQueueItem> queue)
    {
        while (await queue.OutputAvailableAsync())
        {
            var processQueueItem = await queue.ReceiveAsync();
            await this.queueManager.Enqueue(processQueueItem);
            this.tasksEnqueued++;
        }
    }

O gerador de gerar() assinatura é a seguinte:

public void Generate(Guid someId, ITargetBlock<ProcessQueueItem> target)

O que chama a SendAsync() método do alvo para colocar novos itens.O que eu estou fazendo agora, é a divisão do número total de resultados para 'lotes', carregá-los e enviá-los assíncrona, até que tudo é feito:

    public void Generate(Guid batchId, ITargetBlock<ProcessQueueItem> target)
    {
        var accountPromise = this.AccountStatusRepository.GetAccountsByBatchId(batchId.ToString());
        accountPromise.Wait();
        var accounts = accountPromise.Result;

        // Batch configuration
        var itemCount = accounts.Count();
        var numBatches = (int)Math.Ceiling((double)itemCount / this.batchSize);
        Debug.WriteLine("Found {0} items what will be put in {1} batches of {2}", itemCount, numBatches, this.batchSize); 


        for (int i = 0; i < numBatches; i++)
        {
            var itemsToTake = Math.Min(this.batchSize, itemCount - currentIndex);
            Debug.WriteLine("Running batch - skip {0} and take {1}", currentIndex, itemsToTake);

            // Take a subset of the items and place them onto the queue
            var batch = accounts.Skip(currentIndex).Take(itemsToTake);

            // Generate a list of tasks to enqueue the items
            var taskList = new List<Task>(itemsToTake);
            taskList.AddRange(batch.Select(account => target.SendAsync(account.AsProcessQueueItem(batchId))));

            // Return the control when all tasks have been enqueued
            Task.WaitAll(taskList.ToArray());

            currentIndex = currentIndex + this.batchSize;
        } 

Isso funciona no entanto, o meu colega observou: - 'não podemos fazer a interface mais simples, e permitem Gerar() e fazer a interface da seguinte forma:

public Task<IEnumerable<ProcessQueueItem> Generate(Guid someId)

Muito limpo, e sem dependência do método de Gerar para o TPL biblioteca.Eu concordo totalmente, eu só estou com medo de que se eu fizer isso, eu vou ter que ligar para a

var result = Generate().Wait().Result;

em algum ponto, antes de enqueuinig todos os itens.Que vai me fazer esperar até que TODO o material é carregado e está na memória.

Para que a minha pergunta vem para baixo é:como posso começar a usar o EF consulta os resultados assim que eles 'gotejamento em' a partir de uma escolha?Como se a EF seria executar um 'rendimento' sobre os resultados, se você pegar minha deriva.

EDITAR Eu acho que eu fiz um engano pensar.EF carrega os itens preguiçoso por padrão.Então eu só posso devolver todos os resultados como IQueryable<> mas isso não significa que eles estão realmente carregado a partir do DB.Em seguida, vou iterar sobre eles e enfileirar-los.

EDIT 2 Não, isso não funcionar, desde que eu precisa para transformar o objeto do banco de dados no método Generate ()...

Foi útil?

Solução

OK, isso é o que eu acabei com:

    public IEnumerable<ProcessQueueItem> Generate(Guid batchId)
    {
        var accounts = this.AccountStatusRepository.GetAccountsByBatchId(batchId.ToString());

        foreach (var accountStatuse in accounts)
        {
            yield return accountStatuse.AsProcessQueueItem(batchId);
        }
    }

O repositório retorna um IEnumerable de apenas alguns DataContext.Coisas.Onde(...).O gerador usa o método de extensão para transformar a entidade para o modelo de domínio (ProcessQueueItem) que, por meio de rendimento é imediatamente enviado para o chamador do método, que vai começar a chamar o QueueManager para iniciar o enfileiramento.

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