2

当 WCF 对同一操作使用不同的线程时,如何让 WCF 传播 Trace.CorrelationManager 的属性?

我知道 WCF 不保证线程亲和性。所以基本上一个线程可以启动一个请求,另一个线程可以完成它。当我重现此行为时,我看到第一个线程具有正确设置的两个属性 Trace.Correlation.ActivityId 和 Trace.Correlation.LogicalOperationStack。WCF 使用不同的线程完成了操作,但未传播属性。

为了解决这个问题,我可能不得不放弃使用 CorrelationManager 并且可能必须将 ActivityId 存储在 OperationContext 中,我知道它会传播到新线程(如果我在这里错了,请纠正我)。我不想这样做,因为它当然需要更多的工作,而且不如使用单个属性那么优雅。

关于如何解决这个问题的任何其他想法?我可以告诉 WCF 以某种方式为我传播这个吗?

谢谢,穆罕默德

4

1 回答 1

1

为了解决这个问题,我放弃了使用 Trace.CorrelationManager 属性。作为这些属性的替代品,我现在使用添加到扩展类的自定义属性。我现在可以直接修改此扩展上的 ActivityId 和 LogicalOperationStack,可以在任何操作请求的生命周期内访问它们,这与使用 Trace.CorrelationManager 属性一样方便。

另外,我可以存储我想在请求的生命周期中使用的任何其他自定义属性。一个很好的例子是客户 ID 或资源 ID,它也可以用于日志记录以获得更好的可支持性。

using System;
using System.Collections;
using System.ServiceModel;

namespace MyNamespace
{
    /// <summary>
    /// Class that represents an extension used to store custom data for the life of a WCF OperationContext.
    /// </summary>
    public class OperationContextExtension : IExtension<OperationContext>
    {
        /// <summary>The activity id of the operation.</summary>
        private Guid activityId;

        /// <summary>The logical operation stack of the operation.</summary>
        private Stack logicalOperationStack;

        /// <summary>
        /// Initializes a new instance of the OperationContextExtension class.
        /// </summary>
        public OperationContextExtension()
        {
            this.logicalOperationStack = new Stack();
        }

        /// <summary>
        /// Gets the current OperationContextExtension extension from the OperationContext.
        /// </summary>
        public static OperationContextExtension Current
        {
            get
            {
                OperationContextExtension context;
                if (OperationContext.Current == null)
                {
                    context = null;
                }
                else
                {
                    context = OperationContext.Current.Extensions.Find<OperationContextExtension>();
                }

                return context;
            }
        }

        /// <summary>
        /// Gets or sets the activity id for the current operation.
        /// </summary>
        public Guid ActivityId
        {
            get { return this.activityId; }
            set { this.activityId = value; }
        }

        /// <summary>
        /// Gets the LogicalOperationStack for the current operation.
        /// </summary>
        public Stack LogicalOperationStack
        {
            get { return this.logicalOperationStack; }
        }

        /// <summary>
        /// Enables an extension object to find out when it has been aggregated. Called when the extension is added 
        /// to the System.ServiceModel.IExtensibleObject Extensions property.
        /// </summary>
        /// <param name="owner">The extensible object that aggregates this extension.</param>
        public void Attach(OperationContext owner)
        {
            // Use this method for request initialization if needed
        }

        /// <summary>
        /// Enables an object to find out when it is no longer aggregated. Called when an extension is removed 
        /// from the System.ServiceModel.IExtensibleObject Extensions property.
        /// </summary>
        /// <param name="owner">The extensible object that aggregates this extension.</param>
        public void Detach(OperationContext owner)
        {
            // Use this method for request cleanup if needed
        }
    }
}

您现在需要将此扩展添加到 OperationContext。首先,您需要找到适合 WCF 行为扩展的挂钩。一个流行的选项是使用应用于 IEndpointBehavior 的 MessageInspector。这里有一些关于如何实现这一点的精彩读物(如果我的没有帮助,快速搜索将产生许多有用的例子):

一旦你有了你的钩子,你想尽快将你的扩展添加到 OperationContext 中,使用以下行:

// Add an extension object to the current operation context to track custom state across all threads
// for the life of the operation
OperationContext.Current.Extensions.Add(new OperationContextExtension());

现在,您几乎可以从代码工作流中的任何位置访问您的 ActivityId 属性和 LogicalOperationStack,或您在此类中定义的任何其他属性:

LogOperation(OperationContextExtension.Current.ActivityId, logMessage);
OperationContextExtension.Current.LogicalOperationStack.Push("Starting 'Nested Activity' 3...");

希望这可以帮助!

-穆罕默德

于 2012-05-13T04:31:43.470 回答