WCF не распространяет свойства Trace.CorrelationManager при использовании другого потока для той же операции.

Как заставить WCF распространять свойства Trace.CorrelationManager, когда он использует другой поток для той же операции?

Я знаю, что WCF не гарантирует сходство потоков. Таким образом, в основном один поток может начать запрос, а другой поток может его завершить. Когда я воспроизвел это поведение, я увидел, что у первого потока правильно установлены два свойства Trace.Correlation.ActivityId и Trace.Correlation.LogicalOperationStack. WCF завершил операцию с другим потоком, но свойства не были распространены.

Чтобы обойти это, мне, возможно, придется отказаться от использования CorrelationManager и, вероятно, придется сохранить ActivityId в OperationContext, который, как я знаю, распространяется на новый поток (пожалуйста, поправьте меня, если я здесь ошибаюсь). Я не хочу этого делать, так как это, конечно, больше работы и не так элегантно, как использование этого единственного свойства.

Любые другие идеи о том, как я могу обойти это? Могу ли я сказать WCF как-то распространять это для меня?

Спасибо, Мохаммед


person Mohammed Ali    schedule 12.05.2012    source источник


Ответы (1)


Чтобы обойти это, я отказался от использования свойств Trace.CorrelationManager. Вместо этих свойств я теперь использую настраиваемые свойства, добавленные в класс Extension. Теперь я могу напрямую изменить ActivityId и LogicalOperationStack в этом расширении, к которым можно получить доступ на протяжении всего срока действия любого запроса операции, что так же удобно, как и использование свойств Trace.CorrelationManager.

В качестве плюса я могу хранить любые другие настраиваемые свойства, которые я хотел бы использовать на протяжении всего срока действия запроса. Отличным примером этого может быть идентификатор клиента или идентификатор ресурса, который также можно использовать в журнале для лучшей поддержки.

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. Популярным вариантом является использование MessageInspector, применяемого к IEndpointBehavior. Вот несколько замечательных чтений о том, как этого добиться (быстрый поиск даст много полезных примеров, если мои не помогут):

После того, как у вас есть хук, вы хотите добавить свое расширение в 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...");

Надеюсь это поможет!

-Мохаммед

person Mohammed Ali    schedule 13.05.2012