SDK - Asynchronous Searching

  • KM510480
  • 20-Sep-2008
  • 19-May-2010

Archived Content: This information is no longer maintained and is provided "as is" for your convenience.


Asynchronous Searching


Asynchronous Searching

Return to TRIM Web Service Samples

How to perform asynchronous searches in the TCT.

Two Ways to Search

Executing searches in the TRIM Web Service can be performed using two different methods. A search can either be blocking (synchronous) or non-blocking (asynchronous).

Blocking Searches (Synchronous)

A blocking search halts the current thread until the search returns

For instance in the code:

TrimResponse response = engine.Execute(request);

the thread calls engine.Execute, which returns only upon receiving results from the server (or timing out). The advantage of this method is simplicity; it acts exactly as you would expect. The main disadvantage to this method is that the duration of a call to Execute (or any Web Service for that matter) could be very fast or very slow depending on a number of conditions. The last thing you want to have happen is for the entire application to lock up when a request takes to long to complete. This disadvantage prompts the use of non-blocking asynchronous searching.

Non-Blocking Searches (Asynchronous)

An asynchronous search sends off the search request, returns from the function call immediately, and waits for event to fire signaling that results have arrived. This allows control of the thread to be returned to the application while the request takes place.

For instance in the code:



engine.ExecuteAsync(request, ID);

the thread calls engine.ExecuteAsync and the call returns immediately. A short time later, an event will fire alerting that application that results have arrived. To receive notification, the application must set up an event handler.

Basic Setup

To do this, first declare an Engine object that will exist for the life of the search. One way is to make it a data member of the class. For instance:

namespace AsyncTCT{    public partial class Form1 : Form    {        Engine engine = new Engine()// [constructors…]        // [methods…etc]    }}

The next step is to tell the engine object what method will handle the reception of search results. A good place is within the constructor of the class since it is only run once.

public Form1(){    InitializeComponent()// ExecuteCompleted is fired off whenever a response arrives from    // a call to engine.ExecuteAsync    engine.ExecuteCompleted += new ExecuteCompletedEventHandler(engine_ExecuteCompleted);}

Where engine_ExecuteCompleted is a private member function of the same class as seen below:

void engine_ExecuteCompleted(object sender, ExecuteCompletedEventArgs e){}

Now, whenever results return, engine_ExecuteCompleted is called automatically to deal with the them.

A complete handler that could be formed as follows:

void engine_ExecuteCompleted(object sender, ExecuteCompletedEventArgs e){    TrimResponse response = e.Resultforeach (Result result in response.Items)    {        switch (result.GetType().Name)        {           // read results        }    }}

Some Additional Considerations

There are a few additional considerations you have to consider when using asynchronous calls. Consider a scenario where the user is executing multiple searches simultaneously. How will an application using a single event handler know which result set came from which request?

To solve this problem, there is an overloaded method for Engine.ExecuteAsync that allows for an arbitrary object to be passed as a second parameter. Generally, you will supply an ID number or string used to name that particular search, such as

int iSearchID = 1234;Engine.ExecuteAsync(request, iSearchID);

When the event handler is called, the parameter ExecuteCompletedEventArgs e comes with an member called e.UserState, which is the object that you passed to ExecuteAsync. So now when you receive multiple searches being executed simultaneously, you can match up the results with the original search.

Another consideration you should consider is the cancellation of an asynchronous search. Suppose you want to cancel a search before it returns any results. You can do this by using Engine.CancelAsync, which takes the identifier X you passed to Engine.ExecuteAsync(request, X); as its only parameter. As soon as Engine.CancelAsync, the event handler is called and the e.Cancelled member is set to true.

A Full Example

The following is an C# application consisting of:

A Submit Button, A Cancel Button, And a List Box

Clicking Submit runs a title word search for the word “reef” and returns all resord titles with the word reef in it.

Try waiting for a single search, canceling a search, and clicking the submit button multiple times to see how the application behaves.

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Text;using System.Windows.Forms;using AsyncTCT.tctnamespace AsyncTCT{    public partial class Form1 : Form    {        Engine engine = new Engine();        int iRequestID = 0public Form1()        {            InitializeComponent();            engine.ExecuteCompleted += new ExecuteCompletedEventHandler(engine_ExecuteCompleted);        }         void engine_ExecuteCompleted(object sender, ExecuteCompletedEventArgs e)        {            if (e.Cancelled)            {                MessageBox.Show("Search " + (int)e.UserState + " canceled");                return;            }             TrimResponse response = e.Result;             listResults.Items.Add( "Execute ID: " + (int)e.UserState )foreach (Result result in response.Items)            {                switch (result.GetType().Name)                {                    case "SearchResult":                        listResults.Items.Add("SearchResult");                        breakcase "EndResponse":                        listResults.Items.Add("EndResponse");                        breakcase "FetchResult":                        FetchResult fetchRes = (FetchResult)result;                        listResults.Items.Add("FetchResult")foreach (TrimObject obj in fetchRes.Objects)                        {                            foreach (Value property in obj.Values)                            {                                listResults.Items.Add("    " + property.Name + ":\"" + property.Val + "\"");                            }                        }                         breakcase "ErrorResult":                        listResults.Items.Add("ErrorResult");                        break;                    default:                        MessageBox.Show("Unknown Result " + result.GetType().Name.ToString());                        break;                }            }        }         /// <summary>        /// Runs a search        /// </summary>        /// <param name="sender"></param>        /// <param name="e"></param>        private void btnSubmit_Click(object sender, EventArgs e)        {            TrimRequest request = new TrimRequest();             RecordStringSearchClause clause = new RecordStringSearchClause();            clause.Type = RecordStringSearchClauseType.TitleWord;            clause.Arg = "reef";             RecordSearch search = new RecordSearch();            search.Items = new RecordClause[] { clause };             SpecificationProperty rectitleSpec = new SpecificationProperty();            rectitleSpec.Name = "rectitle";             Fetch fetch = new Fetch();            fetch.Items = new SpecificationProperty[] { rectitleSpec };             request.Items = new Operation[] { search, fetch };             engine.Credentials = new System.Net.NetworkCredential(username, password);             iRequestID++;             engine.ExecuteAsync(request, iRequestID);        }         private void btnCancel_Click(object sender, EventArgs e)        {            // Cancels only the last request            engine.CancelAsync(iRequestID);        }    }}