내보내기(0) 인쇄
모두 확장

ListenUri 끝점 동작

업데이트 날짜: 2014년 3월

이 문서의 앞부분에서 알 수 있듯이, Service Bus 큐 또는 구독에서 메시지를 읽도록 WCF 사용자 지정 수신 위치를 정의하는 것은 쉽습니다. 그러나 이전 섹션에서 구독의 메시지를 사용하도록 WCF 서비스 끝점을 정의할 때 항목 URL을 서비스 끝점 주소로 지정하고 구독 URL을 해당 listenUri로 지정해야 한다는 사실을 살펴보았습니다. 이제 WCF 사용자 지정 어댑터를 사용하여 WCF 수신 위치를 구성할 때 구성 대화 상자의 일반 탭에 서비스 끝점 주소를 지정하는 텍스트 상자는 있지만 listenUriListenUriMode 속성 값을 지정하는 필드는 없습니다.

이러한 이유로 런타임에 WCF 사용자 지정 수신 위치에 대한 이러한 값을 설정할 수 있는 사용자 지정 끝점 동작을 만들기로 결정했습니다. 첫 번째 시도는 사용자 지정 끝점 동작에 의해 노출되는 AddBindingParametersApplyDispatchBehavior 메서드를 사용하여 두 메서드에 매개 변수로 전달되는 서비스 끝점의 listenUriListenUriMode를 설정하는 것이었습니다. 그러나 이 기술은 예상대로 작동하지 않았습니다.

결국 다음과 같은 방법으로 문제를 해결했습니다. machine.config에 사용자 지정 끝점 동작을 등록하기 위한 ListenUriBehaviorExtensionElement라는 바인딩 확장 요소 클래스를 만들었습니다. 이 구성 요소는 WCF 사용자 지정 수신 위치를 구성할 때 사용자가 구독 URL을 지정할 수 있게 해주는 listenUri라는 속성을 노출합니다.

런타임에 ListenUriBehaviorExtensionElement 구성 요소는 ListenUriEndpointBehavior 클래스의 인스턴스를 만듭니다. 사용자 지정 끝점 동작의 AddBindingParameters 메서드는 원래 바인딩을 동일한 바인딩 요소를 포함하고 바인딩의 맨 위에 ListenUriBindingElement의 인스턴스를 삽입하는 CustomBinding으로 바꿉니다. 이 방법은 런타임에 사용자 지정 바인딩이 먼저 실행되도록 합니다. 마지막으로, ListenUriBindingElementBuildChannelListener 메서드는 수신 위치 구성에 지정된 URL을 BindingContextListenUriBaseAddress 속성에 할당하고 해당 ListenUriMode 속성 값을 Explicit로 설정합니다. 사용자 편의를 위해 아래에 세 클래스의 코드를 포함시켰습니다. 이 문서 후반부에서 Service Bus 구독에서 메시지를 받는 WCF 사용자 지정 수신 위치를 정의할 때 이 구성 요소를 사용하는 방법을 보여줍니다.

ListenUriBehaviorExtensionElement 클래스

public class ListenUriBehaviorExtensionElement : IEndpointBehavior
{
#region Private Constants
    //***************************
    // Constants
    //***************************

    private const string ListenUriName = "listenUri";
    private const string IsTrackingEnabledName = "isTrackingEnabled";
    private const string ListenUriDescription = "Gets or sets the URI at which the service endpoint listens.";
    private const string IsTrackingEnabledDescription = "Gets or sets a value indicating whether tracking is enabled.";
    #endregion

    #region BehaviorExtensionElement Members
    //***************************
    // Protected Methods
    //***************************

    /// <summary>
    /// Creates a behavior extension based on the current configuration settings.
    /// </summary>
    /// <returns>The behavior extension.</returns>
    protected override object CreateBehavior()
    {
        return new ListenUriEndpointBehavior(ListenUri, IsTrackingEnabled);
    }

    /// <summary>
    /// Gets the type of behavior.
    /// </summary>
    public override Type BehaviorType
    {
        get
        {
            return typeof(ListenUriEndpointBehavior);
        }
    }      
    #endregion

    #region Public Properties
    /// <summary>
    /// Gets or sets the URI at which the service endpoint listens.
    /// </summary>
    [ConfigurationProperty(ListenUriName, IsRequired = true)]
    [SettingsDescription(ListenUriDescription)]
    public string ListenUri
    {
        get
        {
            return (string)base[ListenUriName];
        }
        set
        {
            base[ListenUriName] = value;
        }
    }

    /// <summary>
    /// Gets or sets a value indicating whether the message inspector is enabled.
    /// </summary>
    [ConfigurationProperty(IsTrackingEnabledName, DefaultValue = true, IsRequired = false)]
    [SettingsDescription(IsTrackingEnabledDescription)]
    public bool IsTrackingEnabled
    {
        get
        {
            return (bool)base[IsTrackingEnabledName];
        }
        set
        {
            base[IsTrackingEnabledName] = value;
        }
    }
    #endregion
}

ListenUriEndpointBehavior 클래스

public class ListenUriEndpointBehavior : IEndpointBehavior
{
    #region Private Constants
    //***************************
    // Constants
    //***************************

    private const string ListerUriMessageFormat = "[ListenUriEndpointBehavior] ListenUri = [{0}].";
    #endregion

    #region Public Constructors
    private readonly string listenUri;
    private readonly bool isTrackingEnabled;
    #endregion

    #region Public Constructors
    /// <summary>
    /// Initializes a new instance of the ListenUriEndpointBehavior class.
    /// </summary>
    /// <param name="listenUri">The URI at which the service endpoint listens</param>
    /// <param name="isTrackingEnabled">A boolean value indicating whether tracking is enabled</param>
    public ListenUriEndpointBehavior(string listenUri, bool isTrackingEnabled)
    {
        this.listenUri = listenUri;
        this.isTrackingEnabled = isTrackingEnabled;
    }
    #endregion

    #region IEndpointBehavior Members
    /// <summary>
    /// Implement to pass data at runtime to bindings to support custom behavior.
    /// </summary>
    /// <param name="endpoint">The endpoint to modify.</param>
    /// <param name="bindingParameters">The objects that binding elements require to support the behavior.</param>
    void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
        if (endpoint != null &&
            !String.IsNullOrEmpty(listenUri))
        {
            // Read the binding elements from the original binding
            var bindingElementCollection = endpoint.Binding.CreateBindingElements();
            // Create an array of binding elements
            var bindingElementArray = new BindingElement[bindingElementCollection.Count + 1];
            // Add an instance of the ListenUriBindingElement as first binding element of the array
            bindingElementArray[0] = new ListenUriBindingElement(listenUri);
            // Copy the binding elements of the original binding to the array
            bindingElementCollection.CopyTo(bindingElementArray, 1);
            // Create a custom binding with the same binding elements as the original
            // binding with the addition of the custom binding as first item
            var customBinding = new CustomBinding(bindingElementArray)
                                    {
                                        CloseTimeout = endpoint.Binding.CloseTimeout,
                                        OpenTimeout = endpoint.Binding.OpenTimeout,
                                        ReceiveTimeout = endpoint.Binding.ReceiveTimeout,
                                        SendTimeout = endpoint.Binding.SendTimeout,
                                        Name = endpoint.Binding.Name,
                                        Namespace = endpoint.Binding.Namespace
                                    };
            //Replace the original binding with the newly created binding
            endpoint.Binding = customBinding;
            Trace.WriteLineIf(isTrackingEnabled,
                                string.Format(ListerUriMessageFormat,
                                              listenUri));
        }
    }

    /// <summary>
    /// Implements a modification or extension of the client across an endpoint.
    /// </summary>
    /// <param name="endpoint">The endpoint that is to be customized.</param>
    /// <param name="clientRuntime">The client runtime to be customized.</param>
    void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
    }

    /// <summary>
    /// Implements a modification or extension of the service across an endpoint.
    /// </summary>
    /// <param name="endpoint">The endpoint that exposes the contract.</param>
    /// <param name="endpointDispatcher">The endpoint dispatcher to be modified or extended.</param>
    void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
    }

    /// <summary>
    /// Implement to confirm that the endpoint meets some intended criteria.
    /// </summary>
    /// <param name="endpoint">The endpoint to validate.</param>
    void IEndpointBehavior.Validate(ServiceEndpoint endpoint)
    {
    }
    #endregion
}

ListenUriBindingElement 클래스

public class ListenUriBindingElement : BindingElement
{
    #region Private Fields
    private readonly string listenUri;
    #endregion

    #region Public Constructor
    /// <summary>
    /// Initializes a new instance of the ListenUriBindingElement class.
    /// </summary>
    /// <param name="listenUri">A BindingElement object that is a deep clone of the original.</param>
    public ListenUriBindingElement(string listenUri)
    {
        this.listenUri = listenUri;
    }
    #endregion

    #region BindingElement Members
    /// <summary>
    /// returns a copy of the binding element object.
    /// </summary>
    /// <returns></returns>
    public override BindingElement Clone()
    {
        return new ListenUriBindingElement(listenUri);
    }

    /// <summary>
    /// Returns a typed object requested, if present, from the appropriate layer in the binding stack.
    /// </summary>
    /// <typeparam name="T">The typed object for which the method is querying.</typeparam>
    /// <param name="context">The BindingContext for the binding element.</param>
    /// <returns>The typed object T requested if it is present or nullif it is not present.</returns>
    public override T GetProperty<T>(BindingContext context)
    {
        return context.GetInnerProperty<T>();
    }

    /// <summary>
    /// Returns a value that indicates whether the binding element can build a 
    /// channel factory for a specific type of channel.
    /// </summary>
    /// <typeparam name="TChannel">The type of channel the channel factory produces.</typeparam>
    /// <param name="context">The BindingContext that provides context for the binding element.</param>
    /// <returns>true if the IChannelFactory<TChannel/>of type TChannel can be built by 
    ///          the binding element; otherwise, false.</returns>
    public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
    {
        return false;
    }

    /// <summary>
    /// Initializes a channel factory for producing channels of a specified type from the binding context.
    /// </summary>
    /// <typeparam name="TChannel">The type of channel the factory builds.</typeparam>
    /// <param name="context">The BindingContext that provides context for the binding element.</param>
    /// <returns>The IChannelFactory<TChannel/>of type TChannel initialized from the context. </returns>
    public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
    {
        throw new NotSupportedException();
    }

    /// <summary>
    /// Returns a value that indicates whether the binding element can build a channel 
    /// listener for a specific type of channel.
    /// </summary>
    /// <typeparam name="TChannel">The type of channel listener to build.</typeparam>
    /// <param name="context">The BindingContext for the binding element.</param>
    /// <returns>true if a channel listener of the specified type can be built; 
    ///          otherwise, false. The default is false. </returns>
    public override bool CanBuildChannelListener<TChannel>(BindingContext context)
    {
        return context.CanBuildInnerChannelListener<TChannel>();
    }

    /// <summary>
    /// Initializes a channel listener for producing channels of a specified type from the binding context.
    /// </summary>
    /// <typeparam name="TChannel">The type of channel that the listener is built to accept.</typeparam>
    /// <param name="context">The BindingContext for the binding element.</param>
    /// <returns>The IChannelListener<TChannel/>of type IChannel initialized from the context.</returns>
    public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
    {
        if (!string.IsNullOrEmpty(listenUri))
        {
            context.ListenUriBaseAddress = new Uri(listenUri);
            context.ListenUriMode = ListenUriMode.Explicit;
        }
        return context.BuildInnerChannelListener<TChannel>();
    } 
    #endregion
}

구성 요소 추적

구성 요소 추적을 사용하도록 설정하고 아래 그림에 표시된 것처럼 DebugView를 사용하여 런타임 동작을 모니터링할 수 있습니다.

표시:
© 2014 Microsoft