我希望在控制器方法中请求来自不同端点的数据。我只想在完成所有这些请求后返回View()。可以这样做,怎么做呢?
现在我正在做一些接近这个的事情
class GetDemData
{
int count = 0;
int requestsCompleted = 0;
List<string> addresses = new List<string>();
public void AddDataToBeCollected(string address)
{
adresses.Add(address);
}
public void CollectData()
{
foreach (string address in addresses)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:1337/");
client.GetAsync(address).ContinueWith(
getTask =>
{
if (getTask.IsCanceled)
{
error();
}
else if(getTask.IsFaulted)
{
error();
}
else
{
requestsCompleted++;
checkFinished();
}
}
);
}
}
public void checkFinished()
{
if (count == requestsCompleted)
{
// All data collected
}
}
public void error()
{
// yes error
}
}
这是我的控制器
public ActionResult GetData()
{
var data = new GetDemData();
// fill data with addresses
data.CollectData();
return View();
}
问题是,由于所有内容都是异步完成的,因此会立即返回View。如何确保仅在收集所有数据时返回视图?
答案 0 :(得分:1)
public class GetDemData
{
List<string> addresses = new List<string>();
public void AddDataToBeCollected(string address)
{
adresses.Add(address);
}
public Task CollectData()
{
var webclient = new WebClient();
var tasks = from address in addresses
select webclient.DownloadStringTaskAsync(address);
return Task.WhenAll(tasks.Select(
async (downloadTask) =>
{
var result = await downloadTask;
//Do somthing with result
}));
}
}
public async Task<ActionResult> GetData()
{
var data = new GetDemData();
// fill data with addresses
await data.CollectData();
return View();
}
答案 1 :(得分:0)
您需要保留对Task
实例的引用,然后在该任务上调用.Wait()
以强制线程阻塞,直到完成这些请求。例如:
var mainThread = Task.Factory.StartNew(() =>
{
var tasks = addresses.Select(x => Task.Factory.StartNew(() =>
{
// Do your download stuff for one address
return "someContent";
});
Task.WaitAll(tasks); // Blocks all the minor tasks, waits until they all complete
return tasks.Select(x => x.Result).ToList(); // You may observe exceptions here
});
var allResults = mainThread.Result; // List<string>, blocks until all tasks are complete
// can also observe exceptions here
return View(allResults);
答案 2 :(得分:0)
如果要一次触发所有请求,则无法使用简单的async / await。例如,如果您在循环中使用await
,它仍然会一次触发一个请求,等待它完成,然后触发下一个请求。相反,您必须触发所有请求,收集生成的Task
对象,并等待所有这些对象完成。
public Task CollectData()
{
var tasks = new List<Task>();
foreach (string address in addresses)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://example.com:1337/");
//Note we're collecting the resulting Task objects here
//We're actually getting the task from the continuation, which is a little bit weird
//Alternatively, you could break this into another method that uses await internally
var task = client.GetAsync(address).ContinueWith(
getTask =>
{
if (getTask.IsCanceled)
{
error();
}
else if(getTask.IsFaulted)
{
error();
}
else
{
requestsCompleted++;
checkFinished();
}
}
);
tasks.Add(task);
}
//Return a single task that completes when all the subtasks are done
return Task.WhenAll(tasks.ToArray());
}
...控制器
public async Task<ActionResult> GetData()
{
var data = new GetDemData();
// fill data with addresses
var task = data.CollectData();
await task;
return View();
}
通过使用仅触发一个请求的单独方法来更新,这是一种更简洁的方法。此外,我刚刚注意到您没有正确使用HttpClient
- 它是IDisposable
并且还包含一个内部请求池,因此应该重复使用它,而不是按请求创建和销毁它。
public async Task CollectData()
{
var tasks = new List<Task>();
using (var client = new HttpClient())
{
foreach (string address in addresses)
{
tasks.Add(ExecuteSingleRequest(client, address));
}
await Task.WhenAll(tasks.ToArray());
}
}
private async Task ExecuteSingleRequest(HttpClient client, Uri uri)
{
try
{
var response = await client.GetAsync(uri);
}
catch (Exception ex)
{
//This is lazy example code, do real error handling here and don't catch Exception
}
}