我正在寻找一个执行上下文,它与async/await
以及TPL同时以下列方式(预期行为)很好地匹配:
async Task<string> ReadContext(string slot)
{
// Perform some async code
...
return Context.Read(slot);
}
(1)与async/await
Context.Store("slot", "value");
await DoSomeAsync();
Assert.AreEqual("value", Context.Read("slot"));
Context.Store("slot", "value");
var value = await ReadContext("slot");
Assert.AreEqual("value", value);
(2)与Task.Run()
Context.Store("slot", "value");
var task = Task.Run(() => ReadContext("slot"));
Assert.IsNull(task.Result);
(3)与期待已久的Task
Context.Store("slot", "value");
var value = await Task.Run(() => ReadContext("slot"));
Assert.AreEqual("value", value);
(3)不是必不可少的,但会很好。我现在使用CallContext
,但它在(2)时失败,因为即使在手动运行的任务中也可以访问存储在其中的值,即使在使用Task.Factory.StartNew(..., LongRunning)
运行的那些应该强制运行的任务中也是如此单独一个线程上的任务。
有没有办法实现这个目标?
答案 0 :(得分:4)
您的真实问题在于您的评论:
我需要一个在ASP.NET应用程序中存储NHibernate会话的地方。如果我在请求上下文中,HttpContext工作正常(并且尊重async / await),但是一旦我跳入手动运行任务,它就不可用。
首先,你应该避免在ASP.NET应用程序中“手动运行任务”;我对这个主题有一个blog post。
其次,将内容存储在HttpContext.Items
中是一种黑客行为。它在少数情况下很有用,但IMO管理NHibernate会话应该适合您的应用程序。这意味着您应该在方法调用中传递会话(或提供对会话的访问的服务),或者将其注入到需要它的每种类型中。
所以,我真的认为你所寻找的“背景”是一个错误的解决方案。即使它是可能的,但事实并非如此。
正如@Noseratio指出的那样,要求(2)和(3)不能同时满足。 Task.Run
中执行的代码可以访问,也可以不访问;它不可能都是。
正如您所发现的那样,逻辑调用上下文可以满足要求(1)和(3)(请注意Google员工:此仅适用于.NET 4.5,并且仅当您存储不可变时)数据;详细信息on my blog)。
除非您在FreeNamedDataSlot
中的代码开头手动删除数据(Task.Run
),否则没有简单的方法来满足(1)和(2)。我认为可能还有另一个解决方案,但它需要在每个等待时都进行定制等待,这是完全繁琐,脆弱和不可维护的。