我正在写一些东西,将SQL服务器中的记录加载到azure队列中。问题是,选择结果中的项目数可能非常大,所以我想在仍然检索数据的同时启动排队。
我正在尝试利用EF6(异步),所有异步方法和TPL进行并行排队。所以我有:
// 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);
ReceiveAndEnqueue很简单:
private async Task ReceiveAndEnqueue(ISourceBlock<ProcessQueueItem> queue)
{
while (await queue.OutputAvailableAsync())
{
var processQueueItem = await queue.ReceiveAsync();
await this.queueManager.Enqueue(processQueueItem);
this.tasksEnqueued++;
}
}
生成器generate()签名如下:
public void Generate(Guid someId, ITargetBlock<ProcessQueueItem> target)
调用目标上的SendAsync()方法来放置新项目。我现在正在做的是将结果总数分成'批量',加载它们,然后发送它们异步,直到所有的结果都完成了:
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;
}
然而,我的同事评论说 - “我们不能让界面更简单,让Generate()并使接口像这样:
public Task<IEnumerable<ProcessQueueItem> Generate(Guid someId)
更清洁,并且没有Generate方法依赖于TPL库。我完全同意,我只是害怕,如果我这样做,我将不得不打电话
var result = Generate().Wait().Result;
在某些时候,在enqueuinig所有项目之前。这将使我等待,直到所有的东西被加载并在内存中。
所以我的问题是:一旦他们从选择中滴入'我怎么能开始使用EF查询结果?如果你抓住我的漂移,好像EF会对结果产生'收益'。
修改 我想我犯了一个思维错误。 EF默认加载项目延迟。所以我可以将所有结果作为IQueryable&lt;&gt;返回但这并不意味着它们实际上是从DB加载的。然后我会迭代它们并将它们排队。
编辑2 不,这不起作用,因为我需要在Generate()方法中从数据库转换对象......
答案 0 :(得分:0)
好的,这就是我最终的结果:
public IEnumerable<ProcessQueueItem> Generate(Guid batchId)
{
var accounts = this.AccountStatusRepository.GetAccountsByBatchId(batchId.ToString());
foreach (var accountStatuse in accounts)
{
yield return accountStatuse.AsProcessQueueItem(batchId);
}
}
存储库返回一个只有一些DataContext.Stuff.Where(...)的IEnumerable。生成器使用扩展方法将实体转换为域模型(ProcessQueueItem),该模型通过yield立即发送给方法的调用者,它将开始调用QueueManager开始排队。