Implementing OneWay Operations


When a client application invokes a OneWay operation, it can continue running without waiting for the service to complete the operation. You can indicate that an operation is OneWay by specifying the OneWay behavior in the operation contract. The simplest way to achieve this is to set the IsOneWay property to true in the OperationContract attribute when defining the operation. You will see an example of this in the exercises in this section.

The Effects of a OneWay Operation

Marking an operation as OneWay has several implications. The most important one is that such an operation cannot pass any data back to the client application; it must return a void and cannot have parameters marked as out or ref. When a OneWay operation starts running in the service, the client application has no further contact with it and will not even be aware of whether the operation was successful or not. If the operation raises an exception that would normally generate a SOAP fault message, this SOAP fault message is simply discarded and never sent to the client.

Note 

If you invoke a OneWay operation by using the Request method of an IRequestChannel object as described in Chapter 10, “Programmatically Controlling the Configuration and Communications,” the value of the response message returned will be null.

Invoking a OneWay operation is not simply a matter of generating a message and throwing it at an endpoint where, hopefully, the service is listening. Although a client application does not know when a OneWay operation has completed (successfully or not), it is still important for the client application to know that the service has received the message. If the service is not listening on the specified endpoint, the client will be alerted with an exception. Additionally, if the service is very busy, it might not be able to accept the message immediately. Some transports implement a buffering mechanism for requests and will not accept further messages if the number of outstanding requests is too big. For example, the TcpTransport channel has a configurable property called ListenBacklog that specifies the maximum number of connection requests that can be pending. If this number is exceeded, then subsequent requests will be blocked until the number of pending requests drops; the client application will wait until the request is accepted. If you want to be absolutely certain that the service has received the message and that the transport has not just buffered it, you can invoke the OneWay operation in a reliable session. The service will send back an acknowledgement to the client when it starts processing the message, as described in Chapter 9, “Implementing Reliable Sessions.” Reliable messaging has some other beneficial side effects on OneWay operations, as you will see in the exercise.

OneWay Operations and Timeouts

There are several possible failure points when sending messages over a network. If a client application does not receive a response to a request within a specified period of time, the WCF runtime on the client computer assumes that something has gone wrong and throws a System.TimeoutException to the client application. The duration of this timeout period is configurable as the SendTimeout property of the client binding and has a default value of 1 minute. If a client application invokes a OneWay operation, the service only needs to accept the message within this time period; it does not have to complete processing the message in this time.

Malicious users have often exploited OneWay operations to perform Denial of Service attacks; they bombard a service with a large number of requests, causing it to grind to a halt as it attempts to process all the messages. Bindings have a ReceiveTimeout property, which is used by the WCF runtime managing the service. If an operation takes longer than the amount of time specified by this value to complete, the WCF runtime aborts the operation and throws a Timeout exception. The default value for the ReceiveTimeout property is 10 minutes. This is a long time, and you should consider changing this value unless you genuinely have operations that could take this long to perform.

In the following exercises, you will implement a OneWay operation and investigate what happens when you invoke it from a client application. To provide another variation in the host environment, you will create a new WCF service that runs using the ASP.NET Development Web Server supplied with Visual Studio 2005. Developers frequently use this environment when building new Web services and then deploy them to IIS when they are complete. The Web service you will create will provide administrative functions for the AdventureWorks organization.

The first operation you will implement enables an administrator to request a report of the current day’s sales. This report could take several minutes to run, and you don’t want to hold up the administrator while this is happening, so you will implement this feature as a OneWay operation.

Create the AdventureWorks administrative operations service

  1. Using Visual Studio 2005, create a new Web site using the WCF Service template. In the New Web Site dialog box, select File System from the Location drop-down list box and set the location to the Microsoft Press\WCF Step By Step\Chapter 11\AdventureWorksAdmin folder under your \My Documents folder. Make sure you set the Language to Visual C#.

  2. In Solution Explorer, select the C:\\AdventureWorksAdmin\ project. In the Properties window, set the Use dynamic ports property to False and set the Port number to 9090.

    By default, the ASP.NET Development Web Server picks an unused port when it starts running. For this exercise, it is useful to know in advance exactly which port the service will use, and disabling the dynamic ports property enables you to specify a fixed port.

  3. In Solution Explorer, expand the App_Code folder and edit the image from book Service.cs file.

  4. In the code view window, remove everything underneath the using statements (leave the using statements intact).

  5. Add a new service contract called IAdventureWorksAdmin to the file. In the ServiceContract attribute, set the Namespace property to http://adventure-works.com/2007/01/01 (pretend it is January 1st, and that you use the recommended approach of incorporating the creation date into namespaces), and set the Name property to AdministrativeService, like this:

     [ServiceContract(Namespace="http://adventure-works.com/2007/01/01",                  Name="AdministrativeService")] public interface IAdventureWorksAdmin { }

  6. Add an operation called GenerateDailySalesReport, shown below in bold, to the service contract. Mark it as a OneWay operation by setting the IsOneWay property of the OperationContract attribute to true:

     public interface IAdventureWorksAdmin {     [OperationContract(IsOneWay = true)]     void GenerateDailySalesReport(string id); }

    Notice that this method returns a void. All OneWay methods must be void methods. OneWay methods can take parameters, as long as they are not marked with the ref or out modifiers. The parameter passed to the GenerateDailySalesReport method will contain a string that you will use to identify an invocation of the operation.

  7. In Solution Explorer, right-click the C:\\AdventureWorksAdmin\ project, and then click Add Reference. In the Add Reference dialog box, add a reference to the PresentationFramework assembly and then click OK.

  8. Add a class called IAdventureWorksAdmin to the file, underneath the service contract. This class should implement the IAdventureWorksAdmin interface and provide the GenerateDailySalesReport method, as follows:

     public class AdventureWorksAdmin : IAdventureWorksAdmin {     public void GenerateDailySalesReport(string id)     {         // Simulate generating the report         // by sleeping for 1 minute and 10 seconds         System.Threading.Thread.Sleep(70000);         string msg = String.Format("Report {0} generated", id);         System.Windows.MessageBox.Show(msg);     } }

    In this version of the WCF service, you will simulate the process of generating the report by sleeping for 70 seconds (you will understand why I have selected a duration of just over 1 minute in the next exercise). When the report has been generated, the method displays a message box.

    Important 

    The message box displayed by this method is primarily so you can observe when the method completes. You should never incorporate interactive message boxes like this in a production service. If you need to output messages for testing or debugging purposes, you should generally use the System.Diagnostics.Debug.WriteLine method and send messages to a trace listener.

  9. In Solution Explorer, edit the image from book Service.svc file. Modify this file to refer to the AdventureWorksAdmin class, as shown in bold below:

     <% @ServiceHost Language=C# Debug="true" Service="AdventureWorksAdmin" CodeBehind="~/ App_Code/Service.cs" %>

  10. In Solution Explorer, edit the image from book Web.config file by using the WCF Service Configuration Editor.

  11. In the WCF Service Configuration Editor, select the MyService service in the Services folder. In the right pane, change the Name property of the service to AdventureWorksAdmin.

    Notice that this service also has a behavior defined called returnFaults. You will examine this behavior later in this exercise.

  12. In the left pane, expand the service (the name will still appear as MyService until you click it), expand the Endpoints folder, and click the endpoint labeled (Empty Name). In the right pane, verify that this endpoint uses the wsHttpBinding binding. Set the Name property of the endpoint to AdventureWorksAdminHttpBinding and set the Contract property to IAdventureWorksAdmin.

  13. In the left pane, add a new binding configuration to the Bindings folder based on the wsHttpBinding binding type.

  14. In the right pane displaying the properties of the new binding, set the Name property to AdventureWorksAdminHttpBindingConfig. It should never take more than 5 minutes to generate the daily report, so set the ReceiveTimeout property to 00:05:00.

  15. In the left pane, return to the AdventureWorksAdminHttpBinding endpoint. In the right pane, set the BindingConfiguration property to AdventureWorksAdminHttpBindingConfig.

  16. In the left pane, expand the Advanced folder, expand the ServiceBehaviors folder. Note that the service has a behavior called returnFaults.

    This is the service behavior mentioned earlier. If you examine this service behavior using the WCF Configuration Editor, you will see that it contains a <serviceDebug> element with the IncludeExceptionDetailInFaults property set to True. Leave this element intact for the time being, but bear in mind that you should disable this property of the behavior, or remove the behavior, before deploying the service to a live environment.

  17. Select the returnFaults behavior and then click Add in the right pane. In the Adding Behavior Element Extension Sections dialog box, click serviceMetadata, and then click Add. In the left pane, click the <serviceMetadata> element that has just been added to the returnFaults service behavior. In the right pane, set the HttpGetEnabled property to True (leave the HttpsGetEnabled property set to False).

    This element enables metadata publishing for the service. You will query the metadata for the service when you build the client application for testing this service.

  18. Save the configuration file and exit the WCF Service Configuration Editor.

  19. In Visual Studio 2005, start the solution without debugging.

    If you have not made any mistakes so far, the ASP.NET Development Server will start (if it is not already running) and display an icon in the bottom right corner of the Windows taskbar. Internet Explorer will run, navigate to the site http://localhost:9090/AdventureWorksAdmin/Service.svc, and display the page describing how to generate the WSDL description for the service and how to use the service in a client application, like this:

    image from book

    Tip 

    If Internet Explorer displays a blank page, manually enter the address http://localhost:9090/AdventureWorksAdmin/Service.svc in the address bar and press Enter to display the page for the service.

  20. Close Internet Explorer and return to Visual Studio 2005.

  21. In Solution Explorer, right-click the C:\\AdventureWorksAdmin\ project and then click Start Options. In the Start Options page, select the option “Don’t open a page. Wait for a request from an external application,” and then click OK.

    When you start this project in subsequent exercises, you just need the service to start running but don’t want Internet Explorer to open.

Create a WCF client application for testing the AdventureWorks administrative operations service

  1. In Visual Studio 2005, add a new project to the AdventureWorksAdmin solution. In the Add New Project dialog box, select the Visual C# project types, select the Console Application template, set the Name to AdventureWorksAdminTestClient, and save it in the Microsoft Press\WCF Step By Step\Chapter 11 folder under your \My Documents folder.

  2. In Solution Explorer, right-click the AdventureWorksAdminTestClient project and then click Add Service Reference. In the Add Service Reference dialog box, type http://localhost:9090/AdventureWorksAdmin/Service.svc in the Service URI text box, type AdventureWorksAdmin in the Service reference name text box, and then click OK.

    This step generates the client proxy for the AdventureWorksAdmin WCF service and adds it to the AdventureWorksAdminTestClient project. It also creates an application configuration file.

  3. Open the image from book app.config file and examine its contents. Note that Visual Studio 2005 has added and configured a binding for communication with the AdventureWorksAdmin service called AdventureWorksAdminHttpBinding.

  4. In Solution Explorer, in the AdventureWorksAdminTestClient project, open the file image from book Program.cs. Add the following using statement to the list at the start of the file:

     using AdventureWorksAdminTestClient.AdventureWorksAdmin;

    The proxy class generated in the previous step is in this namespace.

  5. Add the statements shown below in bold to the Main method:

     static void Main(string[] args) {     try     {         AdministrativeServiceClient proxy =             new AdministrativeServiceClient                ("AdventureWorksAdminHttpBinding");         Console.WriteLine("Requesting first report at {0}", DateTime.Now);         proxy.GenerateDailySalesReport("First Report");         Console.WriteLine("First report request completed at {0}",             DateTime.Now);         Console.WriteLine("Requesting second report at {0}", DateTime.Now);         proxy.GenerateDailySalesReport("Second Report");         Console.WriteLine("Second report request completed at {0}",             DateTime.Now);         proxy.Close();     }     catch (Exception e)     {         Console.WriteLine("Exception: {0}", e.Message);     }     Console.WriteLine("Press ENTER to finish");     Console.ReadLine(); }

    This code creates a proxy object and then invokes the GenerateDailySalesReport operation twice in quick succession, displaying the date and time before and after each request.

  6. In Solution Explorer, right-click the AdventureWorksAdmin solution and then click Set Startup Projects. In the Property Pages dialog box, select the Multiple startup projects option. Set the action for both projects to Start, and then click OK.

  7. Start the solution without debugging.

    The request for the first report completes quickly (it might take a few seconds, depending on whether the WCF service is still running or Visual Studio 2005 needs to start it, but it will be less than the 70 seconds that the operation runs for), but the second request causes the client application to stop. If you wait for 1 minute, the client application eventually times out with an error (the default value of the SendTimeout property of the binding is 1 minute):

    image from book

    You should also notice that the AdventureWorksAdmin service successfully completes the first request and displays a message box:

    image from book

    Click OK to close the message box. If you wait for another 70 seconds, the message box for the second request appears as well. This shows that the request was actually received successfully by the AdventureWorksAdmin service, although something appears to have gone awry with the client application.

    Click OK to close the second message box, and press Enter to close the client application console window.

So, what went wrong? The problem lies partly in the fact that the service uses sessions, and partly in the concurrency mode of the service. If you disable sessions (by using a binding that does not support them, or by setting the SessionMode property of the ServiceContract attribute of the IAdventureWorksAdmin interface to SessionMode.NotAllowed), then the concurrent calls to the service are not blocked. However, sessions are a useful feature, especially if you want to make effective use of transactions, or control the sequencing of operations. Consequently, many services make a lot of use of them. If you recall from Chapter 7, “Maintaining State and Sequencing Operations,” services that use sessions are single-threaded by default. This behavior causes the service to block the second request from the client application until the first has completed processing.

Note 

In case you are wondering, setting the service instance context mode to PerCall rather than PerSession (which is the default when using sessions) does not stop the service from blocking requests. The important factor is the use of sessions, and how the service processes requests made in the same session.

The blocking problem is also exacerbated by the fact that the AdventureWorksAdmin service uses the HTTP transport, which does not support request queuing like the TCP transport does. If you had used a TCP endpoint, the client would be able to continue as soon as the second request would be queued by the transport channel, rather than waiting for it to be accepted by the service. The second request would still not be processed until the first had completed, however.

In the next exercise you will see how to address this situation.

Resolve the blocking problem with the OneWay request in the WCF client application

  1. In Solution Explorer, edit the image from book Service.cs file in the App_Code folder in the C:\\AdventureWorksAdmin\ project. Add the ServiceBehavior attribute shown below in bold to the AdventureWorksAdmin class:

     [ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple)] public class AdventureWorksAdmin : IAdventureWorksAdmin {      }

    As described in Chapter 7, the ConcurrencyMode property of the ServiceBehavior attribute enables you to change the threading model used by the session. Selecting the value ConcurrencyMode.Multiple allows the service to process multiple concurrent requests in the same session, although you must ensure that the code you write in each method is thread-safe (as it is in this example).

  2. Start the solution without debugging.

    This time, both requests are submitted successfully (the client application displays the message “Second report request completed at ”), but the client application now stops and times out at the end of the Main method:

    image from book

    Press Enter to close the client console window, and press OK when the two message boxes displayed by the service appear.

    This time, the blockage is caused by a combination of the security implemented by the service and the call to the Close method of the proxy. Remember that the wsHttpBinding binding uses sessions and message-level security by default. When terminating a session, the client application and service exchange messages to ensure that the session terminates in a secure and controlled manner. The client application will not finish until this message exchange completes, and the service does not send its final message until all operations have completed, and hence a timeout occurs. Two possible solutions are to disable security (absolutely not recommended) or to switch to transport-level security (which requires installing a certificate and configuring HTTPS). However, there is another workaround available if you want to use message-level security; you can use reliable sessions.

  3. In the C:\\AdventureWorksAdmin\ project, edit the image from book web.config file by using the WCF Service Configuration Editor. In the WCF Service Configuration Editor, expand the Bindings folder and select the AdventureWorksAdminHttpBindingConfig binding configuration. In the right pane, set the Enabled property in the ReliableSession Properties section to True. Save the file, and close the WCF Service Configuration Editor.

  4. Follow the same procedure to edit the image from book app.config file of the client application and enable reliable sessions in the AdventureWorksAdminHttpBinding binding configuration.

  5. Start the solution without debugging.

    The client should successfully send both requests and close the session without timing out.

In Chapter 9, you investigated the acknowledgement messages sent by the reliable sessions protocol implemented by WCF. The purpose of this protocol is to assure both parties (the client and the service) that the messages they have sent have been received. When the client application closes its session, it does not need to wait for the service to actually complete its processing as long as the service has acknowledged all messages sent by the client application; the Close method can complete before the service actually terminates the session.

Note 

If you need to implement OneWay operations but cannot guarantee the thread-safety of the corresponding methods, you should not set the ConcurrencyMode attribute of the service to ConcurrencyMode.Multiple. Just enable reliable sessions.

When you enable reliable sessions, the client will not wait for a single-threaded service to accept each message before continuing; instead, the client will be able to carry on as soon as the service has acknowledged the message. Message acknowledgements are generated by the WCF runtime hosting the service before the message is dispatched to the service instance; so they are not blocked by a single-threaded service instance.

If you are curious, try disabling multiple-threading for the AdventureWorksAdmin service, but keep reliable sessions enabled and then run the solution. The client application will run without blocking. However, you should observe the difference in the behavior of the service. Previously, both calls to the GenerateDailySalesReport method executed concurrently, and the second message box appeared a couple of seconds after the first. If you use reliable messaging rather than multiple threads, the method calls run sequentially, and the second message box will appear at least 70 seconds after the first.

Recommendations for Using OneWay Methods

You have seen that OneWay methods are a very useful mechanism for improving the performance of a client application by enabling it to continue executing without waiting for the service to complete processing operation requests. However, to maximize the concurrency between a client application and a service, you should bear in mind the following points, summarizing what you have seen in the exercises:

  • Services that don’t use sessions provide the greatest degree of parallelism by default. If a service requires or allows sessions, then depending on how the transport used by the binding buffers messages, the service can block OneWay requests if it is already busy processing a message in the same session. This is true even if the service uses the PerCall service instance context mode; it is the fact that the service uses sessions that causes the service to block requests.

  • Services that use sessions can set the concurrency mode to enable multi-threading, but you must ensure that the operations in the service are thread-safe. Enabling multithreading allows the service to execute requests in the same session simultaneously.

  • Enable reliable sessions to allow a client application to close a connection to a service before the service has completed processing of all outstanding requests.




Microsoft Windows Communication Foundation Step by Step
Microsoft Windows Communication Foundation Step by Step (Step By Step Developer Series)
ISBN: 0735623368
EAN: 2147483647
Year: 2007
Pages: 105
Authors: John Sharp

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net