Pregunta

Estoy escribiendo algo que carga registros de SQL Server a una cola Azure. La cosa es que la cantidad de elementos en el resultado de selección puede ser muy grande, por lo que me gustaría iniciar las cosas de cola mientras se recupera los datos.

Estoy tratando de aprovechar EF6 (ASYNC), todos los métodos de ASYNC y tpl para enqueorización paralela. Así que tengo:

        // 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);

La recepción es simple:

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

La firma generadora () generadora es la siguiente:

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

que llama al método SENDASYNC () en el objetivo para colocar nuevos artículos. Lo que estoy haciendo en este momento está dividiendo el número total de resultados en 'lotes', cargándolos y enviándolos Async, hasta que se haga todo:

    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;
        } 

Esto funciona sin embargo, mi colega comentó: "¿No podemos hacer que la interfaz sea más sencilla, y deje que genere () y haga que la interfaz así:

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

Un lote limpiador, y sin dependencia del método generado en la biblioteca de TPL. Estoy totalmente de acuerdo, solo tengo miedo de que si hago eso, voy a tener que llamar

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

en algún momento, antes de enqueuinigar todos los artículos. Eso me hará esperar hasta que se carguen todas las cosas y está en la memoria.

Entonces, ¿cuál es mi pregunta es: ¿Cómo puedo comenzar a usar los resultados de la consulta de EF tan pronto como se 'goteen en' de un selecto? Como si EF ejecutara un "rendimiento" sobre los resultados si atrapas mi deriva.

editar Creo que hice un error de pensamiento. EF carga artículos perezosos de forma predeterminada. Así que puedo simplemente devolver todos los resultados como IQueryable <> pero eso no significa que realmente se cargan desde el DB. Luego iteraré sobre ellos y los enquejaré.

Editar 2 No, eso no funciona, ya que necesito transformar el objeto de la base de datos en el método generado () ...

¿Fue útil?

Solución

OK, esto es lo que terminé con:

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

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

El repositorio devuelve un iEnumerable de solo algunos datos de DataContext.Stuff.Where (...).El generador utiliza el método de extensión para transformar la entidad al modelo de dominio (ProcessQUEUEITEM), que mediante el medio de rendimiento se envía inmediatamente a la persona que llama del método, que comenzará a llamar al queueManager para iniciar la cola.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top