简体   繁体   English

在PCL和Windows Phone应用程序中实现异步作用域

[英]Implementing asynchronous scoping in PCL and Windows Phone applications

For some weird reason, both the System.Runtime.Remoting.Messaging.CallContext and AsyncLocal classes are only available using the full CLR. 出于某种奇怪的原因,只有使用完整的CLR才能使用System.Runtime.Remoting.Messaging.CallContextAsyncLocal类。 This makes it really hard to do asynchronous scoping while working with Portable Class Libraries or Windows Phone applications, especially since Windows Phone APIs are becoming async-only; 这使得在使用可移植类库或Windows Phone应用程序时进行异步作用非常困难,特别是因为Windows Phone API变得异步; so we don't really have an option in not using async/await. 所以我们没有选择不使用async / await。

What this practically means is that in WPF or WinForms, we can write methods like this: 这实际意味着在WPF或WinForms中,我们可以编写这样的方法:

private async void button_Click(object sender, EventArgs e)
{
    CallContext.LogicalSetData("x", new object());
    await Something();
    var context = CallContext.LogicalGetData("x");
}

In WPF and WinForms, the framework ensures that each click to the same button get their own context and with that can run in isolation. 在WPF和WinForms中,框架确保每次单击到同一个按钮都可以获得自己的上下文,并且可以单独运行。 It's hard to achieve the same using ThreadLocal<T> and [ThreadStatic] since every click will get executed on the UI thread. 使用ThreadLocal<T>[ThreadStatic]很难实现相同的功能,因为每次点击都会在UI线程上执行。

My question is, how would we solve this problem in Windows Phone, Store and other application types that don't support CallContext and AsyncLocal<T> ? 我的问题是,我们如何在Windows Phone,Store和其他不支持CallContextAsyncLocal<T>应用程序类型中解决此问题?

Some background information: 一些背景资料:

Very often we want (business) logic to run in some sort of context. 我们经常希望(业务)逻辑在某种上下文中运行。 This context can contain information that the business logic can use throughout the operation. 此上下文可以包含业务逻辑可以在整个操作中使用的信息。 In a server environment this is really easy to imagine, because you need to run requests in a database transaction, need to access the current user, tenant id, etc. But even in a client application, operations might need to access contextual information such as a correlation id for the current running operation or context for logging. 在服务器环境中,这很容易想象,因为您需要在数据库事务中运行请求,需要访问当前用户,租户ID等。但即使在客户端应用程序中,操作也可能需要访问上下文信息,例如用于记录的当前运行操作或上下文的相关ID。 During such operation (like an click event) we might need to resolve additional services (from our Composition Root ). 在此类操作期间(如点击事件),我们可能需要解析其他服务(来自我们的合成根 )。 For the operation to work successfully, we might need to reuse the same component throughout the entire client operation and that means that our Composition Root needs to be aware of the context it is running. 为了使操作成功,我们可能需要在整个客户端操作中重用相同的组件,这意味着我们的组合根需要知道它正在运行的上下文。

Although all this information can be passed on through the entire system using method calls of the services' public API, this would not only force us to pollute the API of the services in our application with implementation details, it would lead to severe maintenance issues, because we would have to pass through all this information throughout the system, and a simple internal change to one of our components would propagate up the call stack through all the methods calls. 虽然所有这些信息都可以使用服务公共API的方法调用在整个系统中传递,但这不仅会迫使我们使用实现细节污染应用程序中的服务的API,而且会导致严重的维护问题,因为我们必须在整个系统中传递所有这些信息,并且对我们的一个组件的简单内部更改将通过所有方法调用向上传播调用堆栈。 And when it comes to our Composition Root, we definitely don't want to pass though some cache/scope object of our DI library through the application, because that would tightly couple our business logic to an external library. 当涉及到我们的Composition Root时,我们绝对不希望通过应用程序传递我们的DI库的一些缓存/范围对象,因为这会将我们的业务逻辑紧密地耦合到外部库。 Obviously, neither do we want to pass on some sort of service locator . 显然,我们也不想传递某种服务定位器

So implementing scoping using something like CallContext or AsyncLocal<T> is very important in client applications. 因此,在客户端应用程序中使用CallContextAsyncLocal<T>等实现范围界定非常重要。

There is no easy solution for this today, sorry. 对不起,今天没有简单的解决方案。 When Windows Phone (eventually) becomes "Windows 10 on a phone" (ie, with AsyncLocal<T> ), then this will be possible. 当Windows Phone(最终)成为“手机上的Windows 10”(即使用AsyncLocal<T> )时,这将是可能的。 But for now... 但现在...

The easiest way to do this is to pass the context, either explicitly (as a parameter) or implicitly (as a member variable on this ). 做到这一点的最简单的方法是通过上下文中,明确地(作为一个参数)或隐含地(如在一个成员变量this )。

It may also be possible to achieve a limited version of this with custom awaiters. 也可以通过自定义等待者来实现这种限制版本。 But in addition to being horribly complex, that solution would require you to modify every await in your entire app (or use a post-compilation IL rewriting to achieve the same effect). 但除了非常复杂之外,该解决方案还需要您修改整个应用程序中的每个await (或使用后编译IL重写来实现相同的效果)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM