You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复C# MongoDB自动补全输入过快导致的崩溃问题

解决WinForms输入过快导致MongoDB查询崩溃的问题

问题根源

你的代码在TextChanged事件中同步执行MongoDB查询,会阻塞UI线程;输入过快时会触发大量连续的查询任务,不仅导致UI卡顿,还可能因线程冲突或内存访问不当引发System.AccessViolationException。另外,若后续改为异步但未处理线程问题,直接操作控件也会引发跨线程错误。

修复方案

通过异步查询+取消令牌+UI线程安全更新解决,核心逻辑是:取消上一次未完成的查询,异步执行新查询,仅在查询完成且未被取消时更新UI控件。

步骤1:添加字段声明

在窗体类中添加CancellationTokenSource字段,用于管理查询任务的取消:

private CancellationTokenSource _previousCts;

步骤2:重构TextChanged事件处理方法

将同步查询改为异步,加入取消逻辑,并确保UI操作在UI线程执行:

private async void textBox2_TextChanged(object sender, EventArgs e)
{
    string searchTerm = textBoxStudNum.Text.Trim();

    // 取消上一次未完成的查询并释放资源
    _previousCts?.Cancel();
    _previousCts?.Dispose();
    _previousCts = new CancellationTokenSource();
    var currentCts = _previousCts;

    if (!string.IsNullOrWhiteSpace(searchTerm))
    {
        try
        {
            var filter = Builders<StudentRecord>.Filter.Regex(s => s.studentNumber, new BsonRegularExpression(searchTerm, "i"));

            var projection = Builders<StudentRecord>.Projection
                .Include(s => s.studentNumber)
                .Include(s => s.firstName)
                .Include(s => s.lastName)
                .Include(s => s.middleName)
                .Include(s => s.college)
                .Include(s => s.year);

            // 异步执行MongoDB查询,传入取消令牌
            var studentRecords = await _studentRecordCollection
                .Find(filter)
                .Project<StudentRecord>(projection)
                .Limit(10)
                .ToListAsync(currentCts.Token);

            // 检查当前任务是否未被取消,避免更新过时结果
            if (!currentCts.IsCancellationRequested)
            {
                // 确保在UI线程更新控件
                textBoxStudNum.Invoke(new Action(() => UpdateAutoComplete(studentRecords)));
            }
        }
        catch (OperationCanceledException)
        {
            // 任务被取消,无需处理
        }
        catch (Exception ex)
        {
            MessageBox.Show($"查询出错:{ex.Message}");
        }
    }
    else
    {
        // 清空补全建议,确保在UI线程操作
        textBoxStudNum.Invoke(new Action(() =>
        {
            _suggestedStudentNumbers.Clear();
            textBoxStudNum.AutoCompleteCustomSource = null;
        }));
    }
}

// 提取更新AutoComplete的逻辑,复用代码
private void UpdateAutoComplete(List<StudentRecord> studentRecords)
{
    _suggestedStudentNumbers = studentRecords.Select(s => s.studentNumber).ToList();
    var autoComplete = new AutoCompleteStringCollection();
    autoComplete.AddRange(_suggestedStudentNumbers.ToArray());

    textBoxStudNum.AutoCompleteCustomSource = autoComplete;
    textBoxStudNum.AutoCompleteMode = AutoCompleteMode.Suggest;
    textBoxStudNum.AutoCompleteSource = AutoCompleteSource.CustomSource;
}

关键优化点说明

  • 取消令牌管理:每次触发输入事件时,先取消上一次未完成的查询,避免任务堆积,同时释放旧资源。
  • 异步查询:使用MongoDB驱动的ToListAsync()方法,不阻塞UI线程,保证输入操作流畅。
  • UI线程安全:通过Invoke确保控件操作在UI线程执行,避免跨线程访问异常。
  • 取消检查:查询完成后验证任务状态,避免更新已过时的查询结果(比如用户已输入新内容,旧结果无意义)。

之前用取消令牌导致补全无显示的原因

大概率是取消任务后未正确处理后续有效查询,或未在UI线程更新控件。上述方案中,每次输入都会创建新的令牌对象,仅取消上一次任务,同时确保有效查询完成后能在UI线程更新补全列表,不会出现补全消失的问题。

内容的提问来源于stack exchange,提问作者Jeffrey Pascual

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 15:55:58