Question

J'écris quelque chose qui charge des enregistrements de SQL Server sur une file d'attente Azure. La chose est que le nombre d'éléments du résultat sélectionné pourrait être très important. Je tiens donc à démarrer les choses en file d'attente pendant que les données sont toujours récupérées.

J'essaie de tirer parti de EF6 (async), de toutes les méthodes asynchrones et et TPL pour en faiseur parallèle. Donc j'ai:

        // 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 créanceUne est 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 signature génératrice de générateur () est la suivante:

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

qui appelle la méthode Sendasync () sur la cible de placer de nouveaux éléments. Ce que je fais en ce moment, c'est diviser le nombre total de résultats en «lots», les charger et les envoyer async, jusqu'à ce que tout soit effectué:

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

Cela fonctionne cependant, mon collègue a fait remarquer: «Ne pouvons-nous pas simplifier l'interface et laisser générer () et rendre l'interface comme si:

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

Beaucoup de nettoyant et sans dépendance de la méthode génératrice sur la bibliothèque TPL. Je suis totalement d'accord, je viens d'attaquer que si je fais ça, je vais devoir appeler

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

À un moment donné, avant d'enQui tous les articles. Cela me fera attendre jusqu'à ce que toutes les choses soient chargées et est en mémoire.

Alors, quelle est la question de ma question est la suivante: comment puis-je commencer à utiliser les résultats de la requête EF dès qu'ils "goutte" à partir d'une sélection? Comme si EF exécutait un «rendement» sur les résultats si vous attrapez ma dérive.

Modifier Je pense que j'ai fait une erreur de réflexion. EF charge les éléments paresseux par défaut. Donc, je peux simplement retourner tous les résultats comme Iquéryable <> mais cela ne signifie pas qu'ils sont effectivement chargés de la DB. Je vais ensuite iTerez-les sur eux et je les en faisez.

edit 2 Nope, cela ne fonctionne pas, car je dois transformer l'objet de la base de données dans la méthode Generate () ...

Était-ce utile?

La solution

OK, c'est ce que j'ai fini avec:

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

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

Le référentiel renvoie un iSenumerable d'un peu de DataContext.stuff.where (...).Le générateur utilise la méthode d'extension pour transformer l'entité vers le modèle de domaine (ProcessQueUEtem) qui par rapport à un rendement est immédiatement envoyé à l'appelant de la méthode, qui commencera à appeler le Queuanager pour commencer à faire la queue.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top