Skip to content

2.3 Async WCF

3.1 Client Using IAsync Result

This C# WPF application connects to a student database service using WCF and allows users to search for student records asynchronously. It retrieves and displays student details, updating the UI safely across threads. The code uses a delegate for asynchronous searches, ensuring proper resource management by checking if EndInvoke has been called before invoking it to avoid errors.

namespace AsyncClient
{
public delegate Student Search(string value); //delegate for searching
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
// Server contract for the client to create a channel
private StudentBusinessInterface foob;
private Search search; //reference to method
public MainWindow()
{
InitializeComponent();
ChannelFactory<StudentBusinessInterface> foobFactory;
NetTcpBinding tcp = new NetTcpBinding();
//Set the URL and create the connection!
string URL = "net.tcp://localhost:8200/StudentBusinessService";
foobFactory = new ChannelFactory<StudentBusinessInterface>(tcp, URL);
foob = foobFactory.CreateChannel();
//Also, tell me how many entries are in the DB.
TotalNum.Text = foob.GetNumEntries().ToString();
}
private void SearchButthon_Click(object sender, RoutedEventArgs e)
{
search = SearchDB;
// Set the callback
AsyncCallback callback;
callback = this.OnSearchCompletion;
// Call the delegate asynchronously
IAsyncResult result = search.BeginInvoke(SearchBox.Text, callback, null);
// search.EndInoke must be called (and once only) for search.BeginInoke
// In the callback method retrieve the delete object 'search' and call search.Endinvoke
}
// This method will be called asynchronously on SearchButthon_Click()
private Student SearchDB(string value)
{
string name = null;
int id = 0;
string universityName = null;
foob.GetValuesForSearch(value, out name, out id, out universityName);
if (id != 0)
{
Student aStudent = new Student();
aStudent.Name= name;
aStudent.Id= id;
aStudent.University = universityName;
return aStudent;
}
return null;
// Once the value is returned the callback (which is set in SearchButthon_Click) will be called
}
private void UpdateGui(Student aStudent)
{
// Update the GUI Asynchronously
// This method is called in a different thread
// Queue the updates for GUI elements
// This is because, GUI elements cannot be updated apart from the GUI thread (main thread)
SiD.Dispatcher.Invoke(new Action(() =>SiD.Text = aStudent.Id.ToString()));
SName.Dispatcher.Invoke(new Action(() => SName.Text = aStudent.Name));
SUni.Dispatcher.Invoke(new Action(() => SUni.Text = aStudent.University));
}
private void OnSearchCompletion(IAsyncResult asyncResult)
{
Student iStudent=null;
Search search=null;
AsyncResult asyncobj = (AsyncResult)asyncResult;
// Check whether the EndInvoke is called already (if EndInvoke is called twice, it will show an error)
if (asyncobj.EndInvokeCalled == false)
{
// Retreieve the delegate object 'search'
search = (Search)asyncobj.AsyncDelegate;
// Call the EndInvoke on the delegate object
// Retrieve the student object returned in function ('SearchDB') pointed by the delegate 'search'
iStudent = search.EndInvoke(asyncobj);
// Update the GUI (Remember, we are in a different thread, not the GUI thread)
UpdateGui(iStudent);
}
asyncobj.AsyncWaitHandle.Close();
}
}
}

Explanation of asyncobj.EndInvokeCalled == false

In the OnSearchCompletion method, the check for asyncobj.EndInvokeCalled == false ensures that the EndInvoke method is only called once on the asynchronous delegate.

The check for asyncobj.EndInvokeCalled == false is a safeguard to avoid multiple invocations of EndInvoke, which could cause errors or resource leaks.

Here’s why this is important:

Understanding IAsyncResult and EndInvoke:
  • IAsyncResult is the interface returned by BeginInvoke when calling a method asynchronously using a delegate.
  • When the asynchronous operation completes, EndInvoke must be called to retrieve the result and to ensure that resources used during the asynchronous operation are properly cleaned up.
The Purpose of the EndInvokeCalled Check:
  • asyncobj.EndInvokeCalled is a flag provided by the AsyncResult class that indicates whether EndInvoke has already been called on the delegate.
  • The EndInvoke method should only be called once. If EndInvoke is called more than once, it can result in an InvalidOperationException or undefined behavior.
  • By checking asyncobj.EndInvokeCalled == false, the code ensures that EndInvoke is only executed if it hasn’t been called already. This prevents double-calling EndInvoke, which could otherwise lead to errors.
Practical Implications:
  • Safety: This check ensures that the code remains safe and stable, particularly in complex scenarios where multiple asynchronous operations might be managed or when the callback could potentially be invoked multiple times.
  • Resource Management: Properly handling EndInvoke ensures that all resources tied to the asynchronous call are released correctly.