3

我花了差不多两天的时间阅读异步/等待教程和答案Stackoverflow和试图了解C#中的异步和并行执行。尽管如此,我还是无法让它与我的代码一起工作。InvalidCastException当使用任务返回值

我需要什么

执行通过PrincipalSearcher一个Active Directory搜索异步无阻塞的WPF界面。

实施

_activeDirectory
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*/ 
    { 
     ... 
    } 
} 

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

问题

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

System.InvalidCastException occurred HResult=-2147467262
Message=Unable to cast COM object of type 'System.__ComObject' to interface type 'IDirectorySearch'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{109BA8EC-92F0-11D0-A790-00C04FD8D5A8}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)). Source=System.DirectoryServices StackTrace: at System.DirectoryServices.SearchResultCollection.get_SearchObject()
InnerException:

思考

  • 我发现的是,这种异常的无效的SynchronizationContext是相关的,但我不知道看看这可能发生在这里。我还在几行代码上打印了Thread.CurrentThread.ManagedThreadId,它总是返回相同的ID。
  • 我也读过,异步方法不应该返回voidTask<T>,但我不认为这是相关的,因为没有人异步使用SearchButtonClick()。而且如果这个问题甚至相关的话,Task.Run()可能会返回一个Task。整个主题对我来说依然模糊不清。

问题

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

http://stackoverflow.com/questions/18110738/passing-collection-from-backgroundworker-dowork-to-backgroundworker-completed-an –

+0

@Gosha_Fighten我已经试过'PrincipalSearchResult 信息搜索结果=(PrincipalSearchResult )等待searchTask ;'。这并没有解决问题。 – mrplow

回答

2

你不能在另一个线程比,你创建对象使用_activeDirectory

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

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

您还需要确保线程有ApartmentStateApartmentState.STA。有关如何做到这一点,请参阅this question


由于异常立即返回等待的调用。如果任务中发生未处理的异常,那么该任务在那一刻完成。

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

3

await确实等待Task完成。 async\await模式和TPL库错过的唯一一件事是Exception被抛出之内Task在那一刻没有被抛出。它被缓存在TaskException属性中,并且在你想获得结果之后立即抛出。

所以问题在于你在Task内运行的代码不能运行到另一个线程中,就像@NineBerry所说的那样。你必须创建AD -object Task,像这样:

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变量各一次。