【问题标题】:InvalidCastException when using Task return value使用任务返回值时出现 InvalidCastException
【发布时间】:2016-09-27 15:52:05
【问题描述】:

我花了将近两天时间阅读有关 Stackoverflow 的 async/await 教程和答案,并尝试了解 C# 中的异步和并行执行。我仍然无法让它与我的代码一起使用。

我需要什么

通过 PrincipalSearcher 异步执行 Active Directory 搜索,而不阻塞 WPF UI。

实施

protected async void SearchButtonClick()
{
    Task<PrincipalSearchResult<Principal>> searchTask = Task.Run(() => _activeDirectory.FindGroup(searchText.Text));

    PrincipalSearchResult<Principal> searchResult = await searchTask;

    foreach (var foundGroup in searchResult) /*exception thrown here*/
    {
        ...
    }
}

_activeDirectory 的类

public PrincipalSearchResult<Principal> FindGroup(String pattern)
{
    ...
    PrincipalSearchResult<Principal> searchResult = searcher.FindAll();
    return searchResult;
}

问题

  • await 似乎没有等待任务完成。 searchTask.IsCompleted 在 await 行之后是真的,但它不可能是因为它几乎不需要时间来完成,如果我同步运行它,搜索大约需要 5 秒。
  • 如果在 foreach 循环开始时抛出异常:

发生 System.InvalidCastException HResult=-2147467262
消息=无法将“System.__ComObject”类型的 COM 对象转换为 接口类型“IDirectorySearch”。此操作失败,因为 对具有 IID 的接口的 COM 组件上的 QueryInterface 调用 “{109BA8EC-92F0-11D0-A790-00C04FD8D5A8}”由于以下原因而失败 错误:不支持此类接口(HRESULT 异常:0x80004002 (E_NOINTERFACE))。源 = System.DirectoryServices StackTrace: 在 System.DirectoryServices.SearchResultCollection.get_SearchObject()
内部异常:

想法

  • 我发现这种异常与无效的 SynchronizationContext 有关,但我不明白这怎么可能发生在这里。我还在几行代码上打印了 Thread.CurrentThread.ManagedThreadId,它总是返回相同的 id。
  • 我还读到异步方法不应该返回void,而是返回Task&lt;T&gt;,但我认为这与这里无关,因为没有人异步使用 SearchButtonClick()。而Task.Run()probably 确实会返回一个任务,如果这甚至相关的话。整个主题对我来说仍然是模糊的。

问题

  • 为什么await 不等待任务完成?
  • 异常原因是什么?

【问题讨论】:

  • @Gosha_Fighten 我已经尝试过PrincipalSearchResult&lt;Principal&gt; searchResult = (PrincipalSearchResult&lt;Principal&gt;) await searchTask;。这并没有解决问题。

标签: c# multithreading active-directory async-await task-parallel-library


【解决方案1】:

await 确实等待Task 完成。 async\await 模式和 TPL 库中唯一遗漏的是 Exception 被抛出 inside Task 在那一刻没有被正确抛出。它被缓存在 TaskException 属性中,并在您想要获得它的结果后立即被抛出。

所以问题是您在Task 中运行的代码不能像@NineBerry 所说的那样运行到另一个线程中。您必须在Task 内部创建您的AD-object,如下所示:

Task<PrincipalSearchResult<Principal>> searchTask = Task.Run(() =>
{
    // this have to be a local variable inside your task
    var _activeDirectory = GET_THE_AD();
    return _activeDirectory.FindGroup(searchText.Text));
}

或者,您可能必须每次为您的线程创建searcher 变量。

【讨论】:

    【解决方案2】:

    您不能在创建对象的位置以外的其他线程上使用_activeDirectory

    这基于对象内部使用的 COM 主机的实现方式。一些 COM 主机的实现方式使得它们可以在多个线程上使用,而另一些 COM 主机的实现方式使得对象只能在创建它的同一线程上使用。

    您需要更改代码以在您在Task.Run 中执行的代码中创建_activeDirectory,或更改_activeDirectory 的实现以创建它用于访问搜索方法内的活动目录的COM 对象。

    您还需要确保该线程具有ApartmentState ApartmentState.STA。请参阅 this question 了解如何执行此操作。


    由于异常,对 await 的调用立即返回。如果任务中发生未处理的异常,则任务在那个时刻完成。

    搜索被执行,同步执行时需要 5 秒,因为在创建它的同一线程中使用 COM 对象时不会发生异常,如上所述。

    【讨论】:

      【解决方案3】:

      即使在与 Active Directory 搜索相同的线程上创建了一个新的 UserModel 之后,我也遇到了这个异常。问题是IEnumerable&lt;UserModel&gt; 包含一个主线程不知道的嵌套类型UserPrincipal。我可以通过使用删除嵌套类型的.ToList() 来解决此问题。

      _context = new PrincipalContext(ContextType.Domain);
      
      public async Task<IEnumerable<UserModel>> SearchDisplayNameAsync(string searchPhrase, bool enabled = true)
      {
          IEnumerable<UserModel> results = await Task.Run(() => SearchDisplayName(searchPhrase: searchPhrase, enabled: enabled));
          return results.ToList();  // <-- ToList() removes nested type
      }
      
      public IEnumerable<UserModel> SearchDisplayName(string searchPhrase, bool enabled = true)
      {
          UserPrincipal userPrincipal = new UserPrincipal(_context)
          {
              DisplayName = $"{searchPhrase}*",
              Enabled = enabled
          };
      
          using (PrincipalSearcher searcher = new PrincipalSearcher(userPrincipal))
          {
              return searcher.FindAll()
                  .OfType<UserPrincipal>()
                  .Select(u => new UserModel
                  {
                      Guid = (Guid)u.Guid,
                      DisplayName = u.DisplayName,
                      EmailAddress = u.EmailAddress
                  });
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2013-10-10
        • 1970-01-01
        • 2013-05-15
        • 2011-08-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多