エクスポート (0) 印刷
すべて展開

ListenUri エンドポイント動作

更新日: 2014年3月

前の記事で説明したように、Service Bus キューまたはサブスクリプションからメッセージを読み取る WCF-Custom 受信場所を定義することは簡単です。しかし、前のセクションでは、サブスクリプションからのメッセージを処理するために WCF サービス エンドポイントを定義する場合は、サービス エンドポイントのアドレスとしてトピック URL を指定し、その listenUri としてサブスクリプション URL を指定する必要があることを説明しました。WCF 受信場所を WCF-Custom アダプターを使用して構成する場合は、構成ダイアログ ボックスの [全般] タブ上にサービス エンドポイントのアドレスを指定するテキスト ボックスがありますが、listenUri および ListenUriMode プロパティの値を指定するフィールドはありません。

このため、実行時、これらの値を WCF-Custom 受信場所に対して設定できるカスタム エンドポイント動作を作成することにしました。最初の試行では、カスタム エンドポイント動作によって公開される AddBindingParameters および ApplyDispatchBehavior メソッドを使用して、両方のメソッドにパラメーターとして渡される、サービス エンドポイントの listenUri および ListenUriMode を設定しました。しかし、この手法はうまく機能しませんでした。

最終的に、次のように問題を解決しました。ListenUriBehaviorExtensionElement と呼ばれるバインド拡張要素クラスを作成して、カスタム エンドポイント動作を machine.config ファイルに登録しました。このコンポーネントによって listenUri と呼ばれるプロパティが公開され、ユーザーは WCF-Custom 受信場所を構成する際にサブスクリプションの URL を指定できます。

実行時、ListenUriBehaviorExtensionElement コンポーネントは ListenUriEndpointBehavior クラスのインスタンスを作成します。カスタム エンドポイント動作の AddBindingParameters メソッドは、元のバインドを同じバインド要素が含まれている CustomBinding に置き換えて、ListenUriBindingElement のインスタンスをバインドの先頭に組み込みます。これにより、実行時にカスタム バインドが最初に実行されます。最後に、ListenUriBindingElementBuildChannelListener メソッドは、受信場所の構成で指定された URL を BindingContextListenUriBaseAddress プロパティに割り当て、その ListenUriMode プロパティの値を Explicit に設定します。ここでは、3 つのクラスについて以下にコードを示します。記事の後半で、Service Bus サブスクリプションからのメッセージを受信する WCF-Custom 受信場所を定義する場合のこのコンポーネントの使用方法について説明します。

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