我想创建一个在我输入时使用 linq 搜索的任务,如果用户输入另一个字符,它应该取消任务并重新创建搜索,我有以下代码:

private Task SearchChannels;
private CancellationTokenSource cancelSearch;

public void PopulateChannels(string newValue)
{
    IsSearchingChannels = true; //This just shows a progressbar
    if (SearchChannels != null && cancelSearch!= null)
        if (SearchChannels.Status == TaskStatus.Running ||
            SearchChannels.Status == TaskStatus.WaitingToRun ||
            SearchChannels.Status == TaskStatus.WaitingForActivation ||
            SearchChannels.Status == TaskStatus.WaitingForChildrenToComplete)
        {
            cancelSearch.Cancel();
            SearchChannels.Wait();
        }
    cancelSearch = new CancellationTokenSource();
    SearchChannels = new Task(() => Channels = new PagedObservableCollection<Channel>(ContractManager.Channels.Where(x => x.Name.ToLower().StartsWith(newValue)).AsParallel().WithCancellation(cancelSearch.Token).ToList()), cancelSearch.Token); //PagedObservableCollection is just a simple class with a list that keeps all items and an ObservableCollection for current items shown

    SearchChannels.Start();
    SearchChannels.ContinueWith((continuation) => IsSearchingChannels = false); // this just hides the progressbar when done
}

我得到这个异常:
'System.OperationCanceledException' 类型的异常
发生在 System.Core.dll 但未在用户代码中处理

附加信息:操作已取消。

我是任务和取消 token 的初学者,有人可以从这里指导我走正确的道路吗?我基本上希望任务检查它是否已经在运行,取消它,然后使用新值再次运行它(我想让这个“搜索框”功能类似于解决方案资源管理器中的 Visual Studio 搜索,它会在您键入时进行搜索)

最佳答案

首先,您需要创建一个 IObservable<string> 来抽象控件上更改的值。 “最简单”的方法是使用 Subject<string> ,但很可能是错误的方法。

下面是您应该放入 ViewModel 的代码。

IDisposable _searchSubscriber =
    _searchString
         .Buffer(TimeSpan.FromMillisecond(300))
         .Select(searchString =>
                Observable.StartAsync(cancelToken =>
                      Search(searchString, cancelToken)
                ).Switch()
         .ObserveOn(new DispatcherScheduler())
         .Subscribe(results => Channels = results);

public Task<List<Channel>> Search(string searchTerm, CancellationToken cancel)
{
    var query = dbContext.Channels.Where(x => x.Name.StartsWith(searchTerm));
    return query.ToListAsync(cancel);
}

private BehaviorSubject<string> _searchString = new BehaviorSubject<string>("");
public string SearchString
{
    get { return _searchString.Value; }
    set { _searchString.OnNext(value); OnPropertyChanged("SearchString"); }
}

Rx.net 是一个非常强大的库,这当然意味着它确实有一些学习曲线(尽管事实是这很复杂,因为您的问题很复杂)。

让我把它摆出来...
.Buffer(TimeSpan.FromMilliseconds(300)) 对您的查询进行去抖动,因此它每 300 毫秒仅运行一次查询。
Observable.StartAsync(cancelToken => Search(searchString, cancelToken)) 为搜索任务创建一个 Observable,当它被处理时会被取消。
Select(x => ...).Switch() 只取最新的查询结果,并处理最后的查询。
ObserveOn(...) 在使用的​​调度程序上运行以下命令,如果您使用的是 DispatchScheduler ,请确保使用 WPF ,如果您使用 Winforms,请确保使用 WinformsScheduler
Subscribe(results => ...) 对结果做一些事情。

关于c# - 在我输入 linq 时运行任务(如果仍在运行,则取消上一个任务),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25258430/

10-11 22:47
查看更多