在同步的asp.net控制器中使用Task.Run

时间:2014-03-28 10:14:10

标签: c# asp.net-mvc asynchronous asp.net-4.5

是否有一种有效的方式来下载文件并将其保存在"背景"没有阻止mvc控制器中当前执行动作的方式?

目前,我有以下示例代码:

    public ActionResult Index()
    {
        InitiateDownload();

        var model = GetModel();
        return View(model);
    }

    private void InitiateDownload()
    {
        Task.Run(() => DownloadAndSaveFileAsync()).ConfigureAwait(false);
    }
    private async Task DownloadAndSaveFileAsync()
    {
        var response = await GetResponse();

        using (var fileStream = new FileStream("c:\\file.zip", FileMode.Create, FileAccess.Write, FileShare.None))
        {
            await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
        }
    }
    public async Task<HttpResponseMessage> GetResponse()
    {
        var client = new HttpClient();
        client.BaseAddress = new Uri("http://someUrl.com");

        return await client.GetAsync("someResourceEndPoint").ConfigureAwait(false);
    }

我已经阅读了几个你不应该在服务器上使用Task.Run的地方(在工作线程中)所以我想知道这个例子是否可扩展(例如,如果它每秒收到10个请求)或者可靠或者是否存在有什么合适的方法可以做到这一点吗?

由于

1 个答案:

答案 0 :(得分:3)

  

不阻止mvc控制器中当前执行的操作?

答案完全取决于你的意思。

如果您的意思是允许您在下载过程中构建模型,那么您可以简单地这样做:

public ActionResult Index()
{
    var task = DownloadAndSaveFileAsync();
    var model = GetModel();
    await task;
    return View(model);
}

但是,如果您想要在不等待下载的情况下将响应返回给客户端,那就更加复杂了。 I have a blog post that goes into more detail on the subject.如果您确实想在工作完成之前返回回复,那么您需要确定该工作对您的重要程度。

如果您需要进行下载,那么您需要将下载请求放在可靠的队列中(例如,Azure队列或MSMQ)并具有独立的后端(例如,Azure辅助角色或Win32服务)进行实际下载和保存。

如果完成下载并不重要 - 例如,您的系统可以处理偶尔丢失的下载 - 那么您可以使用上面我博客文章中的BackgroundTaskManager类型:

private void InitiateDownload()
{
  BackgroundTaskManager.Run(() => DownloadAndSaveFileAsync());
}

我还建议您在下载完成之前关闭AppDomain时使用BackgroundTaskManager.Shutdown取消令牌进行适当的响应(例如,将消息写入事件日志)。


无论您选择哪种解决方案,都应更改DownloadAndSaveFileAsync以打开文件流以进行异步访问。这是文件流的常见错误;默认情况下,它们是同步的。