0

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?

Rookie
  • 5,179
  • 13
  • 41
  • 65
  • Please be aware that using async code has a tendency to make your own code async as well. You're likely going to split your method up into two parts, the initial code leading up to the async call, and whatever should execute in response to this call returning. Does the objects and methods in question return `Task` or `Task`, are they using `IAsyncResult`, or only a callback mechanism? – Lasse V. Karlsen Sep 20 '16 at 16:48
  • I believe that the lync api is using the IAsyncResult mechanism – Rookie Sep 20 '16 at 16:51
  • How do i change the search method so that i returns a task instead? – Rookie Sep 20 '16 at 16:52
  • [`TaskFactory.FromAsync`](https://msdn.microsoft.com/en-us/library/dd321469(v=vs.110).aspx) if you have to work with existing code that uses `IAsyncResult` that you cannot change, or just rewrite the code if you can change it. For existing code you might want to make an abstraction layer to hide away the goriness of that other framework. – Lasse V. Karlsen Sep 20 '16 at 16:55
  • Why not just call `EndSearch`, rather than using a callback? – Rowland Shaw Sep 21 '16 at 08:41

2 Answers2

1

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.

Peter Bons
  • 26,826
  • 4
  • 50
  • 74
  • I agree that this is a better style, but the library that Snehil is using doesn't have methods that return `Task`s – Andrew Skirrow Sep 20 '16 at 16:51
  • 1
    @AndySkirrow You are correct, but using these methods you can create one. That's why Task.Factory.FromAsync is for. To make a bridge for methods that follow the AsyncCallback pattern. – Peter Bons Sep 20 '16 at 16:54
  • Yes, agreed; but I think it'd be useful to be more explicit about how this works in an answer. My assumption is that the OP isn't that aware of the TPL (which may be wrong). – Andrew Skirrow Sep 20 '16 at 16:57
  • I am very new to C# so trying to read up on the fromasync part to see how to fit it to my use case. The reason i asked for task is so that I can await the results in the webapi controller class – Rookie Sep 20 '16 at 17:23
0

It looks like, by looking at your commented out code, that you tried to provide some sort of callback to your Search function. That would work, it should be of type Action<Microsoft.Lync.Model.SearchResults>

public void Search(string searchKey, Action<SearchResults> callback)
{
   ....
   LyncClient.GetClient().ContactManager.BeginSearch(
             searchKey,
             (ar) =>
             {
                 SearchResults searchResults = LyncClient.GetClient().ContactManager.EndSearch(ar);
                 callback(searchResults);
             });
   ....
}

Then just uncomment the code in your controller:

lync.Search("medina",
        (lyncContacts) =>
        {
            log.Debug("Search Results Callback fired!");
            log.Info("Results found: " + lyncContacts.AllResults.Count);                
        });
Jamiec
  • 133,658
  • 13
  • 134
  • 193
  • Yes I did try to provide a callback & the results return in the callback in the code you showed above. But I am not sure how to pass these results to the WEBAPI .... any thoughts? – Rookie Sep 20 '16 at 16:50