我正试图通过WebAPI调用从RavenDb加载一个文档。当我打开一个异步IDocumentSession并调用LoadAsync时,我没有得到异常或结果,线程立即退出,没有错误代码。
我能够绕过API的所有结构并重现错误。
以下是不起作用的代码:
public IHttpActionResult GetMyObject(long id)
{
try
{
var session = RavenDbStoreHolderSingleton.Store.OpenAsyncSession();
var myObject= session.LoadAsync<MyObject>("MyObject/1").Result;
return Ok(myObject);
}
catch (Exception e)
{
return InternalServerError(e);
}
}
我只是简单地将对象的Id硬编码为1进行测试,但为不存在的对象(如"MyObject/1")调用该函数会得到相同的结果。
但是,此代码有效:
public async Task<IHttpActionResult> GetMyObject(long id)
{
try
{
var session = RavenDbStoreHolderSingleton.Store.OpenAsyncSession();
var myObject= await session.LoadAsync<MyObject>("MyObject/1");
return Ok(myObject);
}
catch (Exception e)
{
return InternalServerError(e);
}
}
我尝试/摆弄过的东西:
- 更改调试中捕获的异常
- 仔细监控Raven Studio,看看我是否能发现任何问题(我没有,但我不确定我找对了地方)
- 在没有附加调试器的情况下运行API,以查看是否发生了错误或Raven Studio中是否出现了某些内容(无更改)
所以我想我偶然发现了一个"修复",但有人能解释为什么其中一个会以如此奇怪的方式失败,而另一个会非常好地工作吗
在实际应用程序中,API调用没有async/await对,但进行调用的代码实际上使用async/await。
这是导致我研究这个问题的失败的存储库类:
public async Task<MyObject> Load(string id)
{
return await _session.LoadAsync<MyObject>(id);
}
失败的第一部分是根据设计,对于ASP.Net异步调用,当您对返回的任务调用Result时,您正在阻塞同步上下文,并且调用需要相同的同步上下文才能返回数据。查看Stephen Cleary的以下链接,其中详细解释了相同的机制。
第二部分之所以有效,是因为这是正确的使用方式,而且它不再陷入死锁。第一部分只能在您使用Console应用程序的情况下工作,该应用程序没有要阻止的同步上下文,即使是像winforms这样的其他UI也会有类似的问题,需要使用代码的第二部分