Snehil Wakchaure Snehil Wakchaure - 2 months ago 42
C# Question

C# how to capture async results?

I am a newbie to C# & am trying to use the Lync SDK to search for a Lync user programmatically to get their status. I am not sure how to pass the results to the webservice response when the async callback gets executed.

This is the code:

Webservice Controller GET endpoint:

// GET: api/Lync/5
public String Get(int id)
{
log.Info("[GET] Search for Lync User: " + id);

Lync lync = new Lync();

////lync.signIn();
Boolean isSignedIn = lync.isUserSignedIn();

if (isSignedIn)
{
log.Info("User is Signed In");
Console.Write("Enter search key : ");
lync.Search("medina");

//lync.Search("medina",
//(lyncContacts) =>
//{
// log.Debug("Search Results Callback fired!");
// log.Info("Results found: " + lyncContacts.Count);
// return lyncContacts.ToString();
//});
//Console.WriteLine(name);
//Console.ReadLine();
return "testUser";
}
else
{
log.Info("User is not Signed In!");
// TODO: Return status 500
return "testUser";
}
//Console.ReadLine();
//return "testUser";
}


The above method calls the business service lync.search(..) which is as follows:

public void Search(string searchKey)
{
List<LyncContact> contactList = new List<LyncContact>();
//List<ContactInformationType> ContactInformationList = new List<ContactInformationType>();
//ContactInformationList.Add(ContactInformationType.Activity);
//ContactInformationList.Add(ContactInformationType.Availability);
// ContactInformationList.Add(ContactInformationType.CapabilityString);

//ContactSubscription contactSubscription = LyncClient.GetClient().ContactManager.CreateSubscription();

Console.WriteLine("Searching for contacts on " + searchKey);

LyncClient.GetClient().ContactManager.BeginSearch(
searchKey,
(ar) =>
{
SearchResults searchResults = LyncClient.GetClient().ContactManager.EndSearch(ar);
if (searchResults.Contacts.Count > 0)
{
log.Info("Search results found: " + searchResults.Contacts.Count);

Console.WriteLine(searchResults.Contacts.Count.ToString() + " found");

foreach (Contact contact in searchResults.Contacts)
{
String displayName = contact.GetContactInformation(ContactInformationType.DisplayName).ToString();
ContactAvailability currentAvailability = (ContactAvailability)contact.GetContactInformation(ContactInformationType.Availability);

Console.WriteLine(
contact.GetContactInformation(ContactInformationType.DisplayName).ToString() + " " + contact.GetContactInformation(ContactInformationType.Availability).ToString());

log.Debug("Display Name: " + displayName);
log.Debug("Availability: " + currentAvailability);
LyncContact lyncContact = new LyncContact.Builder().DisplayName("Snehil").Availability("Busy").build();

contactList.Add(lyncContact);
//done(contactList);
}
return;
}
else
{
log.Info("No Results found!");
//done(contactList);
return;
}
},
null);
} else
{
log.Info("No Results found!");
//done(contactList);
return;
}
},
null);
}


I tried to use Task+await but I am having a hard time trying to figure out how can i return the results from the callback funtion as results from the search method. How do i capture this in the controller class?

Answer

If you would like to use the async/await pattern for this use Task.FromAsync https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskfactory.fromasync(v=vs.110).aspx

Google for examples. Like this:

public async Task<List<LyncContact>> SearchAsync(string searchKey)
{
    List<LyncContact> contactList = new List<LyncContact>();

    Console.WriteLine("Searching for contacts on " + searchKey);

    var cm = LyncClient.GetClient().ContactManager;
    var searchResults = await Task<SearchResults>.Factory.FromAsync<String>(
        cm.BeginSearch,
        cm.EndSearch, searchKey, null);

    if (searchResults.Contacts.Count > 0)
    {

        Console.WriteLine(searchResults.Contacts.Count.ToString() + " found");

        foreach (Contact contact in searchResults.Contacts)
        {
            String displayName = contact.GetContactInformation(ContactInformationType.DisplayName).ToString();
            ContactAvailability currentAvailability = (ContactAvailability)contact.GetContactInformation(ContactInformationType.Availability);

            Console.WriteLine(
                 contact.GetContactInformation(ContactInformationType.DisplayName).ToString() + "   " + contact.GetContactInformation(ContactInformationType.Availability).ToString());

            LyncContact lyncContact = new LyncContact.Builder().DisplayName("Snehil").Availability("Busy").build();

            contactList.Add(lyncContact);
        }
    }

    return contactList
}

In the controller you then can do:

public async Task<String> Get(int id)
{
    log.Info("[GET] Search for Lync User: " + id);

    Lync lync = new Lync();

    ////lync.signIn();
    Boolean isSignedIn = lync.isUserSignedIn();

    if (isSignedIn)
    {
        log.Info("User is Signed In");
        Console.Write("Enter search key : ");
        var lyncContacts = await SearchAsync("medina");

        log.Info("Results found: " + lyncContacts.Count);
        return lyncContacts.ToString();
    }
    else
    {
        log.Info("User is not Signed In!");
        // TODO: Return status 500
        return "testUser";
    }
    //Console.ReadLine();
    //return "testUser";
}

Or you can use a TaskCompletionSource, see http://blog.stephencleary.com/2012/07/async-interop-with-iasyncresult.html (older post but principle is still valid.

Warning

I cannot compile this since I do not have access to the libraries you use. It should work but there might be some compile errors

What does it do

Callbacks are not a nice model to work with, as you have found out. using Tasks and the async/await model are far easier to work with. Luckily for you and me they have created several methods that can act as a bridge between the old and the new model. Using Task.FromAsync you can transform IAsyncResult methods to an easier to use Task based methodology. Stephen Cleary has written some nice blogs about them. See the post I mentioned earlier.