我花了将近两天的时间阅读async / await教程和Stackoverflow上的答案,并尝试了解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
是正确的,但它不可能是因为它几乎没有时间完成,如果我同步运行它,搜索大约需要5秒。 发生System.InvalidCastException HResult = -2147467262
Message =无法转换类型为&#39; System .__ ComObject&#39;的COM对象。至 界面类型&#39; IDirectorySearch&#39;。此操作失败,因为 QueryInterface在具有IID的接口的COM组件上调用 &#39; {109BA8EC-92F0-11D0-A790-00C04FD8D5A8}&#39;由于以下原因而失败 错误:不支持此类接口(HRESULT异常:0x80004002 (E_NOINTERFACE))。 Source = System.DirectoryServices StackTrace: 在System.DirectoryServices.SearchResultCollection.get_SearchObject()
的InnerException:
void
而是Task<T>
,但我不认为这是相关的,因为没有人异步使用SearchButtonClick()。 Task.Run()
可能会返回一个任务,如果它甚至相关。整个主题对我来说仍然模糊不清。await
不等待任务完成? 答案 0 :(得分:3)
;
等待Task
完成。 await
模式和TPL库唯一遗漏的是async\await
被抛出内部 Task
在那一刻没有被抛出。它被缓存在Exception
的{{3}}属性中,并且在您想要得到它的结果之后被抛出。
所以问题是你在Exception
内运行的代码无法运行到另一个线程,正如@NineBerry所说。您必须在Task
内创建Task
- 对象 ,如下所示:
AD
或者,您可能必须每次为您的主题创建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));
}
变量。
答案 1 :(得分:2)
您不能在另一个线程上使用_activeDirectory
而不是您创建对象的位置。
这是基于实现对象内部使用的COM主机的方式。某些COM主机以某种方式实现,以便它们可以在多个线程上使用,其中一些以某种方式实现,以便对象只能在创建它的同一线程上使用。
您需要更改代码以在_activeDirectory
中执行的代码中创建Task.Run
或更改_activeDirectory
的实现以创建用于访问内部活动目录的COM对象搜索方法。
您还需要确保该帖子具有ApartmentState
ApartmentState.STA
。有关如何执行此操作,请参阅this question。
由于异常,对等待的调用立即返回。如果任务中发生未处理的异常,则该任务在此时完成。
执行搜索并在同步执行时占用5秒,因为在与创建它的位置相同的线程中使用COM对象时不会发生异常,如上所述。
答案 2 :(得分:0)
即使在与Active Directory搜索相同的线程上创建新的UserModel
之后,我也遇到了此异常。问题是IEnumerable<UserModel>
包含主线程不知道的嵌套类型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
});
}
}