How to Call WCF Services Synchronously and Asynchronously

来源:互联网 发布:哪个下载软件好 编辑:程序博客网 时间:2024/04/24 23:19
  • Download example - 306.29 KB

    Introduction

    This article is an example to show you how to call WCF services synchronously as well as asynchronously.

    Background

    The Windows Communication Foundation (WCF) is an application programming interface in the .NET Framework for building connected,service-oriented applications. AWCF client has two ways to access the functions provided byWCF services. They aresynchronous and asynchronousWCF calls.

    This article is an example to show you how to access WCF services in both ways, synchronously as well as asynchronously. This article assumes that the readers have some basic understandings of theWCF services. If you are not familiar withWCF services, this link is a good place to get you started. In this article, I will first build aWCF service. I will then show you how to access this service. The following is a screen shot of this example Visual Studio solution in the solution explorer:

    VisualStudio.jpg

    This Visual Studio solution has three .NET projects:

    • The "WCFServiceImplementation" project is a class library that implements theWCF service.
    • The "WCFServiceHost" project is an ASP.NET web application that hosts theWCF service implemented in the "WCFServiceImplementation" project.
    • The "WCFCallExample" project is a WPF application, where I will demonstrate the two ways to access theWCF service.

    This Visual Studio solution is developed in C# in Visual Studio 2008. We will take a look at how theWCF service is implemented first.

    The Implementation of the WCF Service

    VisualStudioImplementation.jpg

    The .NET project "WCFServiceImplementation" is a class library. The entireWCF service is implemented in this project in the "StudentService.cs" file:

    using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Serialization;using System.ServiceModel;using System.Text; namespace WCFServiceImplementation{    [ServiceContract]    public interface IStudentService    {        [OperationContract]        List<Student> GetStudents(int NoOfStudents);    }     [DataContract]    public class Student    {         [DataMember(Order = 1)]        public int StudentID { get; set; }         [DataMember(Order = 2)]        public string StudentName { get; set; }         [DataMember(Order = 3)]        public int Score { get; set; }         [DataMember(Order = 4)]        public DateTime EvaluationTime { get; set; }    }     public class StudentService : IStudentService    {        public List<Student> GetStudents(int NoOfRecords)        {            List<Student> studentList = new List<Student>();             Random rd = new Random();            for (int Idex = 1; Idex <= NoOfRecords; Idex++)            {                Student student = new Student();                student.StudentID = Idex;                student.StudentName = "Student Name No." + Idex.ToString();                student.Score = (int)(60 + rd.NextDouble() * 40);                student.EvaluationTime = System.DateTime.Now;                 studentList.Add(student);            }             System.Threading.Thread.Sleep(10000);             return studentList;        }    }}

    This class library defines the following:

    • A "data contract" "Student",which is the class to store the information for an individual student.
    • A "service contract" "Interface" "IStudentService". This "Interface" defines what functions the WCF service should expose to the clients. In this case, it exposes an "operation contract" "GetStudents".

    The "service contract" "IStudentService" is implemented in the "StudentService" class. When accessing theWCF service, aWCF client can make a WCF call to the "GetStudents" method by passing the number of the student records to the proxy method at the client side. The "GetStudents" method then randomly generates aList of "Student" objects and sends theList of these objects to the caller. You may notice that "System.Threading.Thread.Sleep(10000)" is added to the "GetStudents" method to artificially delay the response of theWCF service, so we can see the difference between the synchronous and asynchronousWCF calls at the client side.

    The WCF Service Host Application "WCFServiceHost"

    The WCF service is hosted in the ASP.NET application project "WCFServiceHost", which is shown as the following in the solution explorer:

    VisualStudioHost.jpg

    In this project, the WCF service is hosted in the "StudentService.svc" file. Since the entire implementation of theWCF service is completed in the "WCFServiceImplementation" class library, the "StudentService.svc" file is as simple as the following:

     <%@ ServiceHost Language="C#" Debug="true"     Service="WCFServiceImplementation.StudentService" %>

    The configuration of the WCF service in the "Web.config" file is the following:

    <system.serviceModel>    <services>      <service behaviorConfiguration="WCFServiceHost.ServiceBehavior"        name="WCFServiceImplementation.StudentService">        <endpoint address="" binding="basicHttpBinding" contract="WCFServiceImplementation.IStudentService">          <identity>            <dns value="localhost" />          </identity>        </endpoint>        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />      </service>          </services>        <behaviors>      <serviceBehaviors>        <behavior name="WCFServiceHost.ServiceBehavior">          <serviceMetadata httpGetEnabled="true" />          <serviceDebug includeExceptionDetailInFaults="true" />        </behavior>      </serviceBehaviors>    </behaviors></system.serviceModel>

    If we now right-click on the file "StudentService.svc" and select "View in Browser" in the solution explorer, a web browser window is open showing that theWCF service is functioning. Theend-point address of theWCF service is shown in the address bar of the browser:

    ViewInBrowser.jpg

    The Example WCF Client Application "WCFCallExample"

    The following is the simple WPF application "WCFCallExample" shown in the solution explorer. This application is the example client application to access theWCF service built before.

    VisualStudioWPFApplicationFull.jpg

    The "Service Reference" "StudentService" is added to the project using the "Adding service reference" utility provided by the Visual Studio. After adding the "StudentService" reference, the Visual Studio will generate all the client proxies for accessing the WCF service. When adding this reference, you will need to click the "Advanced ..." button and open the "Service Reference Settings" window. You need to make sure that the "Generate asynchronous operations" checkbox is checked:

    ProxyGenerateAsync.jpg

    By checking this checkbox, Visual Studio will generate the proxies to support both synchronous and asynchronous calls to theWCF service.

    The "WPF user control" "WCFCallExample" implemented in the "WCFCallExample.xaml" and its code-behind file will be used to demonstrate both synchronous and asynchronous calls to theWCF service using the proxies generated. The "WCFCallExample.xaml" file is shown as the following:

    <UserControl x:Class="WFCCallExample.WCFCallExample"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:Toolkit="http://schemas.microsoft.com/wpf/2008/toolkit"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">        <Grid VerticalAlignment="Stretch"          HorizontalAlignment="Stretch" Margin="10,10,10,10">                <Grid.RowDefinitions>            <RowDefinition Height="23" />            <RowDefinition Height="*" />        </Grid.RowDefinitions>                <StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Right">            <TextBlock Style="{StaticResource SmallBold}">                Select the number of the students:            </TextBlock>                        <ComboBox Margin="10,0,0,0" Name="cmbNumberOfStudents" Width="80"                      SelectionChanged="cmbNumberOfStudents_SelectionChanged" />                        <Button Margin="10,0,0,0" Width="150" Click="WCFServiceCall_Click">                Make WCF service call</Button>                        <Button Margin="10,0,0,0" Width="150" Click="ClickMe_Click">                Click me!</Button>        </StackPanel>                <Toolkit:DataGrid Margin="0,10,0,10" AutoGenerateColumns="False"                          Grid.Row="1" Name="DGStudent" IsReadOnly="True">            <Toolkit:DataGrid.Columns>                <Toolkit:DataGridTextColumn Header="StudentID" Binding="{Binding StudentID}" />                <Toolkit:DataGridTextColumn Header="StudentName" Binding="{Binding StudentName}" />                <Toolkit:DataGridTextColumn Header="Score" Binding="{Binding Score}" />                <Toolkit:DataGridTextColumn Header="EvaluationTime" Binding="{Binding EvaluationTime}" />            </Toolkit:DataGrid.Columns>        </Toolkit:DataGrid>    </Grid></UserControl>

    This "XAML" file defines the following major functional "UI elements":

    • A "ComboBox" to select the number of the students to retrieve from theWCF service.
    • A "DataGrid" to display the information of the students obtained from theWCF service.
    • A "Button" labeled as "MakeWCF service call" to issue theWCF calls.
    • A "Button" labeled as "Click me!" to pop up a simple "Messagebox". This button is used to show if the UI thread is blocked by the WCF call.

    The code-behind file of the "WCFCallExample.xaml" file is the following:

    using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Navigation;using System.Windows.Shapes;using System.ServiceModel; namespace WFCCallExample{    /// <summary>    /// Interaction logic for WCFCallExample.xaml    /// </summary>    public partial class WCFCallExample : UserControl    {        private bool AsynchronousCall = false;         public WCFCallExample(bool AsynchronousCall)        {            InitializeComponent();            this.AsynchronousCall = AsynchronousCall;             cmbNumberOfStudents.Items.Add("****");            cmbNumberOfStudents.SelectedIndex = 0;            for (int Idex = 5; Idex <= 30; Idex = Idex + 5)            {                cmbNumberOfStudents.Items.Add(Idex);            }        }         private void ClickMe_Click(object sender, RoutedEventArgs e)        {            MessageBox.Show("OK, I am clicked ...");        }         private void WCFServiceCall_Click(object sender, RoutedEventArgs e)        {            if (cmbNumberOfStudents.SelectedValue.ToString() == "****")            {                MessageBox.Show("Please select the number of the students to retrieve");                return;            }                     DGStudent.ItemsSource = null;             int NumberOfStudents =                System.Convert.ToInt16(cmbNumberOfStudents.SelectedValue);             if (AsynchronousCall) { MakeAsynchronousCall(NumberOfStudents); }            else { MakeSynchronousCall(NumberOfStudents); }        }         private void cmbNumberOfStudents_SelectionChanged(object sender,            SelectionChangedEventArgs e)        {            DGStudent.ItemsSource = null;        }         private void MakeSynchronousCall(int NumberOfStudents)        {            StudentService.StudentServiceClient WCFClient =                new WFCCallExample.StudentService.StudentServiceClient();                        try            {                WCFClient.Open();                List<StudentService.Student>  Students =                    WCFClient.GetStudents(NumberOfStudents);                 DGStudent.ItemsSource = Students;            }            catch (Exception ex){ MessageBox.Show(ex.Message); }            finally            {                if (WCFClient.State ==                    System.ServiceModel.CommunicationState.Opened)                {                    WCFClient.Close();                }            }        }         private void MakeAsynchronousCall(int NumberOfStudents)        {            BasicHttpBinding basicHttpBinding = new BasicHttpBinding();            StudentService.StudentServiceClient c =                new WFCCallExample.StudentService.StudentServiceClient();            EndpointAddress endpointAddress = c.Endpoint.Address;             StudentService.IStudentService iStudentService =                new ChannelFactory<StudentService.IStudentService>                (basicHttpBinding, endpointAddress).CreateChannel();             AsyncCallback aSyncCallBack =                delegate(IAsyncResult result)            {                try                {                    List<StudentService.Student> Students =                        iStudentService.EndGetStudents(result);                     this.Dispatcher.BeginInvoke((Action)delegate                    { DGStudent.ItemsSource = Students; });                }                catch (Exception ex)                {                    this.Dispatcher.BeginInvoke((Action)delegate                    { MessageBox.Show(ex.Message); });                }            };             try            {                iStudentService.BeginGetStudents(NumberOfStudents,                    aSyncCallBack, iStudentService);            } catch (Exception ex) { MessageBox.Show(ex.Message); }        }    }}

    This code-behind file does the following:

    • It defines a private boolean type instance variable "AsynchronousCall", which will be initiated in the constructor of the class. This boolean variable will be used by the event handling method "WCFServiceCall_Click" to decide if a synchronous call or an asynchronous call should be made to the WCF service when the "Make WCF service call" button is clicked.
    • If a synchronous call should be made, the method "MakeSynchronousCall" is used. It will issue a synchronousWCF call and bind the list of the student information from theWCF service to the "DataGrid".
    • If an asynchronous call should be made, the method "MakeAsynchronousCall" is used. It will issue an asynchronous call and bind the list of the student information from theWCF service to the "DataGrid" in the "AsyncCallback" "delegate".

    Both synchronous and asynchronous calls to the WCF service are pretty simple by using the proxies generated by the Visual Studio. When making a synchronous call, the UI thread that issues the call will be blocked until theWCF service sends the results back to the client. When an asynchronous call is made, the UI thread sends theWCF request and forks a worker thread to wait for the response from theWCF service. The UI thread is not blocked and remains responsive to other user interactions. When the response from theWCF service comes back, the "AsyncCallback" "delegate" is executed to process the WCF service response.

    This "user control" "WCFCallExample" is hosted in the application's main window "MainWnd.xaml":

     <Window x:Class="WFCCallExample.MainWnd"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    Style="{StaticResource WindowDefault}"    Title="{Binding Path=ApplicationName, Mode=OneTime}"        WindowStartupLocation="CenterScreen"        WindowState="Maximized"        Icon="Images/Rubik-Cube.ico">        <Grid VerticalAlignment="Stretch"          HorizontalAlignment="Stretch" Margin="5,20,5,5">        <Grid.RowDefinitions>            <RowDefinition Height="*" />            <RowDefinition Height="20" />        </Grid.RowDefinitions>                <Grid Grid.Row="0">            <Grid.RowDefinitions>                <RowDefinition Height="60" />                <RowDefinition Height="*" />            </Grid.RowDefinitions>                        <Grid Grid.Row="0" HorizontalAlignment="Stretch">                <Grid.RowDefinitions>                    <RowDefinition Height="20" />                    <RowDefinition Height="*" />                </Grid.RowDefinitions>                                <TextBlock Grid.Row="0" Style="{StaticResource AppHeader}"                           HorizontalAlignment="Center"                           Text="{Binding Path=ApplicationName, Mode=OneTime}" />                                <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">                    <TextBlock xml:space="preserve"                               Style="{StaticResource AuthorInformation}">Developed by </TextBlock>                    <TextBlock xml:space="preserve" Style="{StaticResource AuthorInformation}"                               Text="{Binding Path=Author, Mode=OneTime}"></TextBlock>                    <TextBlock xml:space="preserve"                               Style="{StaticResource AuthorInformation}"> on </TextBlock>                    <TextBlock xml:space="preserve" Style="{StaticResource AuthorInformation}"                               Text="{Binding Path=DevelopentDate, Mode=OneTime}">  </TextBlock>                    <TextBlock xml:space="preserve"                               Style="{StaticResource AuthorInformation}"> Version   </TextBlock>                    <TextBlock xml:space="preserve" Style="{StaticResource AuthorInformation}"                               Text="{Binding Path=Version, Mode=OneTime}"></TextBlock>                </StackPanel>            </Grid>                         <Grid Grid.Row="1" x:Name="MainContent" HorizontalAlignment="Stretch"               VerticalAlignment="Stretch" Margin="5, 2, 5, 10">                <TabControl FontSize="12" x:Name="ApplicationMainTab">                    <TabItem                    HeaderTemplate="{StaticResource TabItemHeaderTemplate}">                        <TabItem.Header>                            Call WCF Services Synchronously                        </TabItem.Header>                    </TabItem>                      <TabItem                    HeaderTemplate="{StaticResource TabItemHeaderTemplate}">                        <TabItem.Header>                            Call WCF Services Asynchronously                        </TabItem.Header>                    </TabItem>                </TabControl>              </Grid>        </Grid>          <TextBlock Grid.Row="1" Text="{Binding Path=Copyright, Mode=OneTime}"                   HorizontalAlignment="Center" Style="{StaticResource SmallBold}"                   Foreground="Silver" />    </Grid></Window>

    The above "XAML" file defines two "tab items" in a "TabControl". Each of the "tab items" will host an instance of the "user control" created in the "WCFCallExample.xaml" file. One instance will be initiated to make synchronousWCF calls and the other will be initiated to make asynchronousWCF calls by setting the boolean instance variable "AsynchronousCall" accordingly.

    The code-behind file of the "MainWnd.xaml" file is the following:

    using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Shapes;using WFCCallExample.Properties; namespace WFCCallExample{    /// <summary>    /// Interaction logic for MainWnd.xaml    /// </summary>    public partial class MainWnd : Window    {        public MainWnd()        {            InitializeComponent();            this.Loaded += new RoutedEventHandler(MainWnd_Loaded);        }         void MainWnd_Loaded(object sender, RoutedEventArgs e)        {            DataContext = Settings.Default;             ((TabItem)ApplicationMainTab.Items[0]).Content                = new WCFCallExample(false);             ((TabItem)ApplicationMainTab.Items[1]).Content                = new WCFCallExample(true);        }    }}

    The "MainWnd_Loaded" event handler initiates two instances of the "WCFCallExample" "user control" objects and associates each of them to the appropriate "tab item". One instance of the "user control" object is initiated to make synchronous calls, and the other is initiated to make asynchronous calls.

    Run the Application

    Now, we finish the development of this example application. Set the "WCFCallExample" project as the "startup project" and press "F5", you can debug run the application. Let us first take a look at the "CallWCF Services Synchronously" tab:

    WCFCallSynchronized.jpg

    Select the number of the students to retrieve from the WCF service and click the "Make WCF service call" button, a synchronousWCF call is made and the student list is retrieved from theWCF service. During the time when you wait for theWCF service to respond to your call, you can click on the "Click me!" button, you will notice that the UI thread of the WPF application is blocked, and the application will not respond to your button click until theWCF call is finished.

    Let us now take a look at the "Call WCF Services Asynchronously" tab:

    WCFCallAsynchronized.jpg

    After selecting the number of the students to retrieve and clicking the "Make WCF service call" button, an asynchronous WCF call is made. If you can click on the "Click me!" button, you will notice that the UI thread is not blocked and the "MessageBox" telling you the button is clicked shows up immediately even before the WCF service responds back to the WCF call.

    Points of Interest

    • This article is an example to show you how to build a WCF service and how to call the WCF service synchronously and asynchronously as well.
    • When building the WCF service, I separated the implementation and the host of the service. This is not always necessary. This article only shows you that you can separate the concerns of the implementation and the hosting, if you want do it.
    • To call a WCF service synchronously, you can simply use the proxy method created by the Visual Studio, similar to calling a local method.
    • To call a WCF service asynchronously, you will need to first create an "AsyncCallback" "delegate" and pass this delegate to the asynchronous proxy method generated by the Visual Studio. Since the "AsyncCallback" "delegate" is executed in a worker thread different from the UI thread, any access to the "UI elements" needs to be "Dispatched".
    • From the application point of view, either way has its advantages and disadvantages. For most applications, after the calling of aWCF service, the application will be in an intermediate state until the response from the service is received. During this time, it may be ideal to block the UI thread to prevent user actions to get into any un-predictable results. In this case, a synchronous call may be appropriate. In some other scenarios, particularly when we work on some "SOA" applications, we may need to make multiple service calls at the same time and let the services to work in parallel. In this case, asynchronousWCF calls will be the ideal.
    • One thing just came to my mind. To make an asynchronous call, you probably do not need to have the asynchronous proxies. You can well fork a worker thread by yourself and let the thread to do a synchronous call. You will achieve the same effect as using the asynchronous proxies.
    • If you are not familiar with WCF services,this link is a good place to get you started.

    History

    This is the first revision of this article.

    License

    This article, along with any associated source code and files, is licensed underThe Code Project Open License (CPOL)

    About the Author

    Dr. Song Li

    United States United States
    I have been working in the IT industry for some time. It is still exciting and I am still learning. I am a happy and honest person, and I want to be your friend.

     

     

     

    原文链接: http://www.codeproject.com/Articles/91528/How-to-Call-WCF-Services-Synchronously-and-Asynchr 

  • 原创粉丝点击