我在.Net上使用MVC 5,我的用户流程如下:
我知道对Bing地图的调用应该在异步上下文中进行,这样我的网站速度就会与Bing地图的速度脱钩。
相反,我认为我的流程应该是这样的:
我被困在第4步。这就是我所拥有的:
public ActionResult GetLocation(int id)
{
Listing li = db.Listings.Find(id);
Task.Run(() => {
// update geocode if necessary
if (li.BizAddress.GeoStatus != BusinessAddress.GeocodeStatus.UpToDate &&
DateTime.Now - li.BizAddress.LastGeoAttempt > TimeSpan.FromHours(1))
{
Geocoder geo = new Geocoder();
GeocodeResult gr = geo.Geocode(li.BizAddress).Result;
if (gr.BadResult != true)
{
li.BizAddress.Latitude = gr.Location.Latitude;
li.BizAddress.Longitude = gr.Location.Longitude;
li.BizAddress.GeoStatus = BusinessAddress.GeocodeStatus.UpToDate;
}
else
{
// failed
li.BizAddress.Latitude = 0;
li.BizAddress.Longitude = 0;
li.BizAddress.GeoStatus = BusinessAddress.GeocodeStatus.BadResult;
}
li.BizAddress.LastGeoAttempt = DateTime.Now;
db.SaveChanges();
}
});
return PartialView("~/Views/Listing/ListingPartials/_Location.cshtml", li);
}
但是当我到达db.SaveChanges()时,我得到了一个例外,即db上下文已被处理掉。
我希望我的Task在闭包内部运行async,这样db仍然是一个有效的未被置换的变量。
这可能吗?我是异步编程的新手,我还不知道所有的习语。
其他信息:我在我的视图中渲染我的面板:
@{Html.Action("GetLocation", new { id = Model.ID });}
我想保留这种行为,因为用AJAX加载它可能会损害SEO。
答案 0 :(得分:0)
以这种方式解雇任务并不是一种好习惯。相反,将ActionResult签名标记为async
,然后将await
标记为方法正文中所需的结果。这样,您就不会占用等待Bing响应的服务器资源。这也让你不必担心指示客户端轮询未来的结果,因为我认为这是异步编程中的反模式。在一次通话中处理所有内容并在结果可用时发送结果要容易得多。由于您使用的是EF6,因此您还可以利用异步方法来提高服务器性能。
public async Task<ActionResult> GetLocation(int id)
{
var listing = await db.Listings.FindAsync(id);
...
var gr = await geo.Geocode(li.BizAddress);
至少,如果您确定要沿着这条路走下去,那么清理任务代码非常重要。在服务器线程中锁定服务器线程时,使用.Result与Web服务器内的任务是非常糟糕的做法。更改任务的定义,使其为异步:
Task.Run(async () => {
// update geocode if necessary
if (li.BizAddress.GeoStatus != BusinessAddress.GeocodeStatus.UpToDate &&
DateTime.Now - li.BizAddress.LastGeoAttempt > TimeSpan.FromHours(1))
{
Geocoder geo = new Geocoder();
GeocodeResult gr = await geo.Geocode(li.BizAddress);