使用 MEF 在 WCF Web API 中建模客户端上下文

发布于 2024-12-15 04:03:23 字数 962 浏览 3 评论 0原文

我需要在每个请求开始时提取几个标头值,并将它们放入 ClientContext 对象中,该对象可以通过 MEF 注入到我的应用程序代码中。我正在使用 WCF Web API 的预览版 5,但没有找到执行此操作的方法。

在“标准”WCF 中,我将创建一个实现 IExtension的类。并具有以下属性将它们连接在一起:

[Export(typeof(IClientContext)]
[PartCreationPolicy(CreationPolicy.NonShared)]
public static ClientContextExtension Current
{
    get
    {
        var operationContext = OperationContext.Current;

        if (operationContext == null)
            return null;

        var extension = operationContext.Extensions.Find<ClientContextExtension>();

        if (extension == null)
        {
            extension = new ClientContextExtension();

            operationContext.Extensions.Add(extension);
        }

        return extension;
    }
}

自定义 DelegatingHandler 调用 ClientContextExtension.Current 并根据标头值设置属性。不幸的是,对于 WCF Web API,OperationContext.Current 始终为 null!

我无法找到一种方法来使其与 Web API 一起工作。任何帮助表示赞赏!

I need to extract several header values at the start of each request and place them into a ClientContext object that can be injected into my application code by MEF. I am using Preview 5 of the WCF Web API and don't see a way to do this.

In 'standard' WCF, I would create a class that implements IExtension<OperationContext> and have the following property to wire it all together:

[Export(typeof(IClientContext)]
[PartCreationPolicy(CreationPolicy.NonShared)]
public static ClientContextExtension Current
{
    get
    {
        var operationContext = OperationContext.Current;

        if (operationContext == null)
            return null;

        var extension = operationContext.Extensions.Find<ClientContextExtension>();

        if (extension == null)
        {
            extension = new ClientContextExtension();

            operationContext.Extensions.Add(extension);
        }

        return extension;
    }
}

A custom DelegatingHandler calls ClientContextExtension.Current and sets the properties from the header values. Unfortunately, with WCF Web API, OperationContext.Current is always null!

I cannot figure out a way to make this work with the Web API. Any help is appreciated!!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

薄情伤 2024-12-22 04:03:23

我已经提出了一个可行的解决方案,但仍对其他选择持开放态度。首先,原始方法背后的一些基本原理...

因为 WCF 使用线程池,所以基于每线程模型的任何内容都可能(并且将会)具有超出单个请求的生命周期。我需要一种方法来存储从每个请求的 HTTP 标头中提取的客户端上下文信息,因为每次信息都会不同。这意味着我无法保留每个线程的上下文信息,因为该线程将被重新使用。

或者我可以吗?

我的逻辑缺陷是线程重用是问题所在。实际上,每个线程一次只服务一个请求,从而使该线程中的任何信息与该请求隔离。因此,我需要做的就是确保信息与该请求相关并且我的问题得到解决。

我的解决方案是重构 Current 属性以引用标有 [ThreadStatic()] 属性的私有静态字段,确保每个实例都特定于线程。然后,在为每个请求执行的 DelegatingHandler 中,我重置了该请求的对象属性。在该请求期间对 Current 的后续调用将返回特定于请求的信息,并且线程处理的下一个请求将在 DelegatingHandler 中更新,因此就我的其他代码而言,上下文是每个请求的。

并不完美,但它至少让我暂时开始运行。正如我所说,我对其他解决方案持开放态度。

更新

经过仔细检查,该解决方案不起作用,因为 DelegatingHandler 和使用上下文对象的服务代码之间没有线程关联。因此,有时我对检索 ThreadStatic 对象的调用会按预期工作,但在其他情况下,我会得到一个新实例,因为代码在与处理程序不同的线程上运行。

因此,请忽略此解决方案。回到绘图板。

更新我的更新

在与 Glenn Block 讨论我的问题后,事实证明,这只是确保上下文设置在请求处理程序(服务)正在执行的同一线程上的问题。解决方案是使用 HttpOperationHandler 而不是 MessageHandler。

根据 Glenn 的说法,消息处理程序是异步操作的,这意味着它们可以在与请求处理程序(服务)不同的线程上执行,因此我们永远不应该在需要线程关联的消息处理程序中执行任何操作。另一方面,操作处理程序与请求处理程序在同一线程上同步运行,因此我们可以依赖线程关联。

因此,我只是将代码从 MessageHandler 移至 HttpOperationHandler 并获得了所需的结果。

您可以在此处阅读完整说明:http ://sonofpirate.blogspot.com/2011/11/modeling-client-context-in-wcf-web-api.html

I've come up with a working solution but remain open to other options. First, some rationale behind the original approach...

Because WCF uses thread pooling, anything based on a per-thread model may (and will) have a lifetime that extends beyond an individual request. I needed a way to store client context information pulled from the HTTP headers for each request as the information will be different each time. This means I can't persist the context information per-thread because the thread will be re-used.

Or can I?

The flaw in my logic was that thread re-use was the problem. In reality, each thread is only every servicing a single request at one time thereby making any information in that thread isolated to that request. Therefore, all I need to do is make sure that the information is relavent to that request and my problem is solved.

My solution was to refactor the Current property to reference a private static field marked with the [ThreadStatic()] attribute, ensuring that each instance was specific to the thread. Then, in my DelegatingHandler, which executes for each request, I reset the properties of the object for that request. Subsequent calls to Current during that request return the request-specific information and the next request handled by the thread gets updated in the DelegatingHandler so as far as my other code is concerned, the context is per-request.

Not perfect, but it at least gets me up and running for the moment. As I said, I am open to other solutions.

UPDATE

Upon closer inspection, this solution is not working as there is no thread affinity between the DelegatingHandler and the service code that is making use of the context object. As a result, sometimes my call to retrieve the ThreadStatic object works as expected but on other occasions I get a new instance because the code is operating on a different thread than the handler.

So, disregard this solution. Back to the drawing board.

UPDATE TO MY UPDATE

After discussing my problem with Glenn Block, it turns out that it is just a matter of making sure the context is set on the same thread the request handler (the service) is executing. The solution is to use an HttpOperationHandler instead of a MessageHandler.

According to Glenn, message handlers operate asynchronously which means they could execute on a different thread from the request handler (service) so we should never do anything in a message handler that requires thread affinity. On the other hand, operation handlers run synchronously on the same thread as the request handler, therefore we can rely on thread affinity.

So, I simply moved my code from a MessageHandler to an HttpOperationHandler and have the desired results.

You can read a full explanation here: http://sonofpirate.blogspot.com/2011/11/modeling-client-context-in-wcf-web-api.html

满意归宿 2024-12-22 04:03:23

您可以尝试使用

HttpOperationHandler<HttpRequestMessage, HttpRequestMessage>

在那里您应该能够访问标头。

You can try to use a

HttpOperationHandler<HttpRequestMessage, HttpRequestMessage>

There you should be able to access the headers.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文