【发布时间】:2014-02-15 15:45:50
【问题描述】:
我有一个在主应用程序线程中运行的方法,但会为长时间运行的操作创建新的 Task,而 awaits 是它的结果。
public async Task<CalculatorOutputModel> CalculateXml(CalculatorInputModel input)
{
// 1
return await Task.Factory.StartNew(
new Func<CalculatorOutputModel> (() =>
{
// 2
using (var ms = Serializer.Serialize(input))
{
ms.Position = 0;
using (var rawResult = Channel.RawGetFrbXmlOutputs(ms)) // 3
{
var result = Parser.Parse(rawResult);
return result;
}
}
}),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
我的问题是第 (1) 点和 (2) 点中的 AppContext “有点”不同。在第 1 点中,Current 属性中有通常的应用程序上下文,其中包含我需要的所有数据。在第 2 点,如您所知,AppContext.Current 中有 null 引用,因此我无法访问任何数据。第 2 点中访问上下文的问题似乎很简单,只需在局部变量中“捕获”当前上下文或将其作为参数传递即可。对我来说这个问题更加困难,因为我需要在标记为“3”的行的深处访问上下文。
类本身派生自System.ServiceModel.ClientBase<TChannel>,而我需要访问上下文的地方是实现IClientMessageInspector 的类。
class CalculatorMessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
if (AppContext.Current != null)
{
// never goes here
}
}
}
澄清一下,这里是调用堆栈(我不能在所需的方法中传递我的上下文):
所以:
- 我无法将上下文传递给
Channel的方法,因为它没有任何意义; - 我无法从
Channel的上下文中保存所需参数,因为它是代理类; - 我无法在
CalculatorMessageInspector中保存所需的参数,因为它是在当前上下文已经为空的地方创建的。
任何人都可以建议我如何在另一个线程中保持在同一上下文中的任何方法吗?或者,至少,如何从方法层次结构中标记为“2”的位置传递参数?也许,我可以使用SynchronizationContext 以某种方式实现它?非常感谢您的任何建议。
更新
AppContext.Current 认为与HttpContext.Current != null ? HttpContext.Current.Items[AppContextKey] : null 相同
更新 2
因此,在这种情况下,似乎没有通用的解决方案来持久化上下文。因此,在这种具体情况下(非常具体)唯一适用的解决方案是使用闭包捕获所需的参数,然后保存在一个对象中,该对象将在所需的方法中可用(对我来说,向IEndpointBehavior 的实现者添加属性,但该解决方案有点奇怪)。因此,最适用的解决方案是丢弃同步调用的异步包装,因此AppContext 永远不会“消失”。标记斯蒂芬的答案是正确的。
【问题讨论】:
-
你不能传递上下文,但你能改变方法签名吗?如果可以,那么您可以将委托传递给 BeforeSendRequest(),然后从可以访问当前 AppContext 的代码块中执行该委托中的一些代码。
-
什么是
AppContext? -
只是一个评论,你应该使用
Task.Run和async-await就像斯蒂芬在这里解释:blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx -
@DavidKhaykin 我无法更改 BeforeSendRequest 签名,因为这是IClientMessageInspector 接口的成员。因此,不幸的是,情况并非如此。无论如何谢谢;)
-
@StephenCleary 这点很好,我错过了。
AppContext.Current是我们内部类的静态get-ter,但查看源代码表明,在所描述的情况下,这等效于System.Web.HttpContext.Current.Items[AppContextKey]。所以它可以在我的问题中用HttpContext.Current替换AppContext.Current。
标签: c# wcf async-await c#-5.0 applicationcontext