我很难尝试处理来自大量数据库查询的结果。现在他们都在同一时间处理。
有一个客户列表(现在约有40个),每个客户端都有自己的数据库,需要查询所有客户端(使用EF-6)。存储库上的查询方法返回一个Task。这些任务是在循环中创建的。
public Task<List<Employee>> FindByNameOrBirthDateAsync(string surName, DateTime? birthDate)
{
var query = ApplicationContext.Employees
.Include(e => e.Address)
.Include(e => e.CorporateEntities);
if (!string.IsNullOrWhiteSpace(surName))
query = query.Where(e => e.Surname == surName);
if (birthDate != null)
query = query.Where(e => e.BirthDate == birthDate);
return query.ToListAsync();
}
此代码由服务调用,而服务又由Web-API控制器调用。
public class SearchService
{
public List<Task<List<Employee>>> SearchOverAllClients(List<Client> clients, string surName, DateTime? birthDate)
{
return clients.Select(client => FindEmployees(client.Id, surName, birthDate)).ToList();
}
private Task<List<Employee>> FindEmployees(int clientId, string surname, DateTime? birthDate)
{
ApplicationUoW unit = new ApplicationUoW(clientId);
return unit.Employees.FindByNameOrBirthDateAsync(surname, birthDate);
}
}
在控制器Task.WhenAny()中,当结果可用时,将结果返回给客户端,并将它们写入输出流。
我已经在每次迭代时使用Fiddler进行测试,结果一次发送一次。所以我不认为问题出在流编写代码中。
public HttpResponseMessage Get([FromUri]string surname = "", [FromUri]DateTime? birthDate = null)
{
List<Client> clients = _clientsService.GetClients();
List<Task<List<Employee>>> tasks = _searchService.SearchOverAllClients(clients, surname, birthDate);
HttpResponseMessage response = Request.CreateResponse();
response.Content = new PushStreamContent(
async (outputStream, httpContent, transportContext) =>
{
try
{
while (tasks.Count > 0)
{
var task = await Task.WhenAny(tasks);
tasks.Remove(task);
List <Employee> employees = await task;
string responseContent =
await Task.Factory.StartNew(() => JsonConvert.SerializeObject(employees, Formatting.None, new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }));
var buffer = Encoding.UTF8.GetBytes(responseContent);
await outputStream.WriteAsync(buffer, 0, buffer.Length);
await outputStream.FlushAsync();
}
}
catch (HttpException ex) when (ex.ErrorCode == -2147023667)
{
//The remote host closed the connection.
return;
}
finally
{
outputStream.Close();
}
});
return response;
}
此代码的问题在于,它会在处理结果之前等待所有任务完成。虽然我想在他们准备好后立即处理它们。
我一直在尝试使用aysnc / await的不同用法来代替此代码的多种变体,但到目前为止所有尝试都没有成功。
更新 我已根据评论中的建议删除了不需要的并行代码。
答案 0 :(得分:0)
我认为你要找的是ConfigureAwait(false)
。
ASP.NET以一种保证SynchronizationContext
代码在整个执行过程中的任何给定时间都在单个线程(但不一定是同一个线程)上运行的方式实现async
。这意味着您现在发布的代码将全部在单个线程上运行。因此,例如,如果所有查询任务立即完成,var task = await Task.WhenAny(tasks)
之后的回调代码将不会并行运行 - 每个回调将一个接一个地同步运行。
通过转换为:await Task.WhenAny(tasks).ConfigureAwait(false)
,您告诉.NET可以在线程池线程上运行回调代码。但是,您必须确保await
之后的代码是线程安全的,并且不需要上下文。
如果您还不熟悉SynchronizationContext及其在使用async/await
时的重要性,请参阅以下文章:
Await, SynchronizationContext, and Console Apps