异步DB-Query触发存储过程

时间:2015-04-09 08:56:17

标签: c# asp.net entity-framework tsql asynchronous

我想在C#中执行异步数据库查询,该查询调用备份的存储过程。由于我们使用Azure,因此大约需要2分钟,我们不希望用户等待那么久。

因此,我们的想法是使其异步,以便在请求之后继续运行任务。

[HttpPost]
public ActionResult Create(Snapshot snapshot)
{
    db.Database.CommandTimeout = 7200;
    Task.Run(() => db.Database.ExecuteSqlCommandAsync("EXEC PerformSnapshot @User = '" + CurrentUser.AccountName + "', @Comment = '" + snapshot.Comment + "';"));
    this.ShowUserMessage("Your snapshot has been created.");
    return this.RedirectToActionImpl("Index", "Snapshots", new System.Web.Routing.RouteValueDictionary());
}

我担心我不理解异步设备的概念。如果我不使用wait语句,查询将不会被执行(或中止?)。但实际上“等待”是我特别想做的一件事。

所以...为什么我被迫在这里等待?

或者该方法是否会启动,但如果请求完成则会被杀死?

4 个答案:

答案 0 :(得分:1)

  

我们不希望用户等那么久。

async-await无法帮助您。听起来很奇怪,基本async-await模式是关于以非阻塞方式实现同​​步行为 。它没有重新安排你的逻辑流程;事实上,它需要付出很大的努力来保护它。你在这里进行async改变的唯一一点是你在2分钟的数据库操作期间不再占用一个线程,如果你有很多并发用户,这是一个巨大的赢得你的应用程序的可扩展性,但是没有加快个人请求。

我认为您真正想要的是将操作作为后台作业运行,以便您可以立即响应用户。但是要小心 - 在ASP.NET中有bad ways这样做(即Task.Run)并且有good ways

答案 1 :(得分:0)

戴夫,你不是被迫在这里等待。你是对的 - 从用户的角度来看,它还需要2分钟。唯一的区别是处理您的请求的线程现在可以处理其他请求,同时数据库可以完成其工作。当数据库完成时,线程将继续处理您的请求。

假设您有足够数量的线程能够处理HTTP请求。此异步代码将帮助您在每个时间段处理更多请求,但它无法帮助用户更快地完成工作。

答案 2 :(得分:0)

这似乎归结为对asyncawait做什么的误解。

async并不意味着run this on a new thread,实质上它是编译器构建状态机的信号,所以这样的方法:

Task<int> GetMeAnInt()
{
    return await myWebService.GetMeAnInt();
}

有点(不能强调这一点),变成了这个:

Task<int> GetMeAnInt()
{
    var awaiter = myWebService.GetMeAnInt().GetAwaiter();
    awaiter.OnCompletion(() => goto done);
    return Task.InProgress;
    done:
        return awaiter.Result;
}

MSDN has way more information关于此问题,甚至还有一些代码解释了如何建立自己的等待者。

asyncawait只是让你能够编写使用回调的代码,但是以一种很好的方式告诉编译器为你做繁重的工作。

如果你真的想在后台运行某些东西,那么你需要使用Task

Task<int> GetMeAnInt()
{
    return Task.Run(() => myWebService.GetMeAnInt());
}

OR

Task<int> GetMeAnInt()
{
    return Task.Run(async () => await myWebService.GetMeAnInt());
}

第二个示例在lambda中使用async和await,因为在此方案中,Web服务上的GetMeAnInt也恰好返回Task<int>

回顾一下:

  1. asyncawait只是指示编译器执行一些jiggerypokery
    1. 这会使用带有goto
    2. 的标签和回调
    3. 有趣的是,这是有效的IL但是C#编译器不允许它用于你自己的代码,因此为什么编译器可以逃脱魔法,但你不能。
  2. async并不意味着&#34;在后台线程上运行&#34;
  3. Task.Run()可用于对线程池线程进行排队以运行任意函数
  4. Task.Factory.Start()可用于获取一个全新的线程来运行任意函数
  5. await指示编译器这是awaiter awaitable的结果(例如任务)为awaited的点 - 这就是它知道如何构建状态机。

答案 3 :(得分:0)

正如我在MSDN article on async ASP.NET中描述的那样,async不是银弹;它不会改变HTTP协议:

  

当一些开发人员了解异步并等待时,他们认为这是服务器代码“屈服”到客户端(例如,浏览器)的一种方式。但是,async和等待ASP.NET只能“屈服”到ASP.NET运行时; HTTP协议保持不变,每个请求仍然只有一个响应。

在您的情况下,您尝试使用Web请求启动后端操作,然后返回浏览器。 ASP.NET不是为了执行像这样的后端操作而设计的。它只是一个Web层框架。让ASP.NET执行工作很危险,因为ASP.NET只知道来自其请求的工作。

我的博客上有overview of various solutions。请注意,使用普通Task.RunTask.Factory.StartNewThreadPool.QueueUserWorkItem 非常危险,因为ASP.NET对此工作一无所知。在至少,你应该使用HostingEnvironment.QueueBackgroundWorkItem,所以ASP.NET至少知道这项工作。但这并不能保证工作真的能够完成。

正确的解决方案是将工作放在一个持久队列中,并拥有一个独立的后台工作进程队列。请参阅Asynchronous Messaging Primer(具体而言,您的方案是“解耦工作负载”)。