【问题标题】:C# how to capture async results?C#如何捕获异步结果?
【发布时间】:2016-09-20 16:27:38
【问题描述】:

我是 C# 的新手,我正在尝试使用 Lync SDK 以编程方式搜索 Lync 用户以获取他们的状态。我不确定在执行异步回调时如何将结果传递给 web 服务响应。

这是代码:

Web 服务控制器 GET 端点:

        // 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";
    }

上述方法调用业务服务lync.search(..)如下:

        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);
    }

我尝试使用 Task+await,但我很难弄清楚如何将回调函数的结果作为搜索方法的结果返回。我如何在控制器类中捕获它?

【问题讨论】:

  • 请注意,使用异步代码也会使您自己的代码异步。您可能会将您的方法分成两部分,导致异步调用的初始代码,以及响应此调用返回应执行的任何内容。有问题的对象和方法是否返回TaskTask&lt;T&gt;,它们是使用IAsyncResult,还是仅使用回调机制?
  • 我认为lync api使用的是IAsyncResult机制
  • 如何更改搜索方法以便我返回任务?
  • TaskFactory.FromAsync 如果您必须使用无法更改的使用 IAsyncResult 的现有代码,或者如果可以更改则只需重写代码。对于现有代码,您可能希望创建一个抽象层来隐藏其他框架的血腥。
  • 为什么不直接调用EndSearch,而不是使用回调?

标签: c# asynchronous lync-2013 lync-client-sdk


【解决方案1】:

如果您想为此使用 async/await 模式,请使用 Task.FromAsync https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskfactory.fromasync(v=vs.110).aspx

谷歌的例子。像这样:

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
}

然后你可以在控制器中做:

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";
}

或者你可以使用TaskCompletionSource,见http://blog.stephencleary.com/2012/07/async-interop-with-iasyncresult.html(旧帖但原则仍然有效。

警告

我无法编译它,因为我无权访问您使用的库。它应该可以工作,但可能会有一些编译错误

它有什么作用

正如您所发现的,回调不是一个很好的模型。使用 Tasks 和 async/await 模型更容易使用。对你和我来说幸运的是,他们创造了几种方法,可以作为新旧模型之间的桥梁。使用 Task.FromAsync,您可以将 IAsyncResult 方法转换为更易于使用的基于任务的方法。 Stephen Cleary 写了一些关于它们的不错的博客。见我前面提到的帖子。

【讨论】:

  • 我同意这是一种更好的样式,但是 Snehil 使用的库没有返回 Tasks 的方法
  • @AndySkirrow 你是对的,但是使用这些方法你可以创建一个。这就是 Task.Factory.FromAsync 的原因。为遵循 AsyncCallback 模式的方法搭建桥梁。
  • 是的,同意;但我认为更明确地说明它在答案中的工作方式会很有用。我的假设是 OP 并没有意识到 TPL(这可能是错误的)。
  • 我对 C# 非常陌生,因此尝试阅读 fromasync 部分以了解如何使其适合我的用例。我要求任务的原因是我可以等待 webapi 控制器类中的结果
【解决方案2】:

通过查看您注释掉的代码,您似乎试图为您的Search 函数提供某种回调。可以,它应该是Action&lt;Microsoft.Lync.Model.SearchResults&gt;

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

然后取消注释控制器中的代码:

lync.Search("medina",
        (lyncContacts) =>
        {
            log.Debug("Search Results Callback fired!");
            log.Info("Results found: " + lyncContacts.AllResults.Count);                
        });

【讨论】:

  • 是的,我确实尝试提供回调,结果在您上面显示的代码中的回调中返回。但我不确定如何将这些结果传递给 WEBAPI .... 有什么想法吗?
猜你喜欢
  • 1970-01-01
  • 2014-10-02
  • 2021-08-18
  • 2015-10-21
  • 2019-04-12
  • 1970-01-01
  • 1970-01-01
  • 2011-08-12
  • 2013-04-20
相关资源
最近更新 更多