方法: チャネル ファクトリを使用して、非同期的に操作を呼び出す

ここでは、ChannelFactory<TChannel> ベースのクライアント アプリケーションを使用する場合に、クライアントからサービス操作に非同期にアクセスする方法について説明します。 サービスを呼び出すために System.ServiceModel.ClientBase<TChannel> オブジェクトを使用する場合は、イベント ドリブンの非同期呼び出しモデルを使用できます。 詳細については、「方法: サービス操作を非同期に呼び出す」を参照してください。 イベント ベースの非同期呼び出しモデルの詳細については、「イベント ベースの非同期パターン (EAP)」を参照してください。

このトピックのサービスは、ICalculator インターフェイスを実装しています。 クライアントはこのインターフェイスにある操作を非同期に呼び出すことができます。これはたとえば Add という操作を BeginAddEndAdd の 2 つのメソッドに分割できることを意味します。前者によって呼び出しを開始し、後者によって操作の完了時に結果を取得します。 サービスで操作を非同期で実装する方法を示す例については、「方法: 非同期サービス操作を実装する」を参照してください。 同期操作と非同期操作の詳細については、「同期操作と非同期操作」を参照してください。

手順

WCF サービス操作を非同期に呼び出すには

  1. 次のコマンドに示すように、/async オプションを指定して ServiceModel メタデータ ユーティリティ ツール (Svcutil.exe) を実行します。

    svcutil /n:http://Microsoft.ServiceModel.Samples,Microsoft.ServiceModel.Samples http://localhost:8000/servicemodelsamples/service/mex /a
    

    これにより、操作の非同期クライアント版のサービス コントラクトが生成されます。

  2. 次のサンプル コードに示すように、非同期操作の完了時に呼び出されるコールバック関数を作成します。

    static void AddCallback(IAsyncResult ar)
    {
        double result = ((CalculatorClient)ar.AsyncState).EndAdd(ar);
        Console.WriteLine("Add Result: {0}", result);
    }
    
    Private Shared Sub AddCallback(ByVal ar As IAsyncResult)
        Dim result = (CType(ar.AsyncState, CalculatorClient)).EndAdd(ar)
        Console.WriteLine("Add Result: {0}", result)
    End Sub
    
  3. サービス操作に非同期にアクセスするには、次のサンプル コードに示すように、クライアントを作成して Begin[Operation] (たとえば BeginAdd) を呼び出し、コールバック関数を指定します。

    ChannelFactory<ICalculatorChannel> factory = new ChannelFactory<ICalculatorChannel>();
    ICalculatorChannel channelClient = factory.CreateChannel();
    
    // BeginAdd
    double value1 = 100.00D;
    double value2 = 15.99D;
    
    IAsyncResult arAdd = channelClient.BeginAdd(value1, value2, AddCallback, channelClient);
    Console.WriteLine("Add({0},{1})", value1, value2);
    
    Dim factory As New ChannelFactory(Of ICalculatorChannel)()
    Dim channelClient As ICalculatorChannel = factory.CreateChannel()
    
    ' BeginAdd
    Dim value1 = 100.0R
    Dim value2 = 15.99R
    
    Dim arAdd As IAsyncResult = channelClient.BeginAdd(value1, value2, AddressOf AddCallback, channelClient)
    Console.WriteLine("Add({0},{1})", value1, value2)
    

    コールバック関数が実行されると、クライアントは End<operation> (EndAdd など) を呼び出して結果を取得します。

上記の手順で使用したクライアント コードで使用するサービスは、次のコードに示すように ICalculator インターフェイスを実装しています。 サービス側では、コントラクトの Add 操作と Subtract 操作が、Windows Communication Foundation (WCF) ランタイムにより同期して呼び出されますが、クライアントでは、上記のクライアントの手順が非同期で呼び出されます。 Multiply 操作と Divide 操作は、クライアントで同期して呼び出される場合でも、サービス側ではサービスを非同期に呼び出すように使用されます。 この例では、AsyncPattern プロパティを true に設定します。 このプロパティ設定と .NET Framework 非同期パターンの実装の組み合わせにより、ランタイムに対して操作が非同期的に呼び出されるよう指示されます。

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface ICalculator
{
    [OperationContract]
    double Add(double n1, double n2);

    [OperationContract]
    double Subtract(double n1, double n2);

    //Multiply involves some file I/O so we'll make it Async.
    [OperationContract(AsyncPattern = true)]
    IAsyncResult BeginMultiply(double n1, double n2, AsyncCallback callback, object state);
    double EndMultiply(IAsyncResult ar);

    //Divide involves some file I/O so we'll make it Async.
    [OperationContract(AsyncPattern = true)]
    IAsyncResult BeginDivide(double n1, double n2, AsyncCallback callback, object state);
    double EndDivide(IAsyncResult ar);
}
<ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _
Public Interface ICalculator
    <OperationContract> _
    Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double

    <OperationContract> _
    Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double

    'Multiply involves some file I/O so we'll make it Async.
    <OperationContract(AsyncPattern:=True)> _
    Function BeginMultiply(ByVal n1 As Double, ByVal n2 As Double, ByVal callback As AsyncCallback, ByVal state As Object) As IAsyncResult
    Function EndMultiply(ByVal ar As IAsyncResult) As Double

    'Divide involves some file I/O so we'll make it Async.
    <OperationContract(AsyncPattern:=True)> _
    Function BeginDivide(ByVal n1 As Double, ByVal n2 As Double, ByVal callback As AsyncCallback, ByVal state As Object) As IAsyncResult
    Function EndDivide(ByVal ar As IAsyncResult) As Double
End Interface