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 byBeginInvoke
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 theAsyncResult
class that indicates whetherEndInvoke
has already been called on the delegate.- The
EndInvoke
method should only be called once. IfEndInvoke
is called more than once, it can result in anInvalidOperationException
or undefined behavior. - By checking
asyncobj.EndInvokeCalled == false
, the code ensures thatEndInvoke
is only executed if it hasn’t been called already. This prevents double-callingEndInvoke
, 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.