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

WinUI 3(.NET 7)SQL异步操作报错:值不在预期范围内

问题分析与解决方案

首先明确:你最初遇到的**"Value does not fall within the expected range"错误并非数据库异步操作导致**,调用栈明确指向ItemsControl.set_ItemsSource,本质是UI线程绑定的线程安全问题;后续出现的await挂起则是异步操作上下文阻塞导致的死锁。以下是针对性解决方案:


1. 修复UI绑定的线程安全问题

WinUI的UI元素必须在Dispatcher线程上更新,直接在构造函数中同步等待异步方法并赋值ItemsSource会触发线程错误。

错误示例(MainPage构造函数)

// 错误:同步等待异步任务导致线程上下文冲突
public MainPage()
{
    InitializeComponent();
    var vm = new AsyncViewModel();
    var result = vm.GetAllStarsAsync().Result; // 阻塞UI线程,引发后续绑定错误
    InventoryList.ItemsSource = result;
}

正确异步初始化方式

改用Loaded事件触发异步操作,并确保在Dispatcher线程上更新UI:

public MainPage()
{
    InitializeComponent();
    Loaded += MainPage_Loaded;
}

private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    var vm = new AsyncViewModel();
    var stars = await vm.GetAllStarsAsync();
    // 显式切换到UI线程赋值,避免跨线程访问UI元素
    await DispatcherQueue.EnqueueAsync(() => 
    {
        InventoryList.ItemsSource = stars;
    });
}

2. 优化ViewModel的异步集合操作

ObservableCollection的修改必须在UI线程执行,建议在ViewModel中声明可绑定的集合属性,避免手动赋值ItemsSource:

using Microsoft.Data.SqlClient; // 替换旧版System.Data.SqlClient
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Dispatching;

public partial class AsyncViewModel : ObservableRecipient
{
    [ObservableProperty]
    private ObservableCollection<Star> _stars = new();

    private readonly DispatcherQueue _dispatcherQueue;

    public AsyncViewModel(DispatcherQueue dispatcherQueue)
    {
        _dispatcherQueue = dispatcherQueue;
    }

    public async Task LoadStarsAsync()
    {
        var cnStr = @"Data Source=.\SQLExpress;Initial Catalog=Company;Integrated Security=True;MultipleActiveResultSets=True";
        const string query = "select * from Star";

        using var cn = new SqlConnection(cnStr);
        await cn.OpenAsync();
        using var cmd = new SqlCommand(query, cn);
        using var reader = await cmd.ExecuteReaderAsync();

        // 清空现有数据(必须在UI线程)
        await _dispatcherQueue.EnqueueAsync(() => Stars.Clear());

        while (reader.Read())
        {
            var star = new Star
            {
                Id = (int)reader["Id"],
                Name = (string)reader["Name"]
            };
            // 新增元素到ObservableCollection(必须在UI线程)
            await _dispatcherQueue.EnqueueAsync(() => Stars.Add(star));
        }
    }
}

对应XAML绑定

<ListView x:Name="InventoryList" ItemsSource="{x:Bind ViewModel.Stars, Mode=OneWay}" />

MainPage调用

private readonly AsyncViewModel _viewModel;

public MainPage()
{
    InitializeComponent();
    _viewModel = new AsyncViewModel(DispatcherQueue.GetForCurrentThread());
    Loaded += async (s, e) => await _viewModel.LoadStarsAsync();
}

3. 解决SqlClient异步死锁问题

若遇到await挂起(死锁),核心原因是:WinUI的SynchronizationContext会捕获UI线程上下文,同步等待(.Result/.Wait())会导致上下文阻塞。需遵守以下规则:

  • 绝对禁止在UI线程使用.Result或.Wait()等待异步任务
  • 所有数据库异步操作全程使用await,不阻塞执行流
  • 若需更新UI,必须通过DispatcherQueue切换到UI线程

4. 替换为Microsoft.Data.SqlClient

System.Data.SqlClient是旧版库,对WinUI和.NET 7的兼容性较差,建议替换为官方推荐的Microsoft.Data.SqlClient:

  1. 卸载项目中的System.Data.SqlClient引用
  2. 通过NuGet安装Microsoft.Data.SqlClient包
  3. 修改代码中的命名空间为using Microsoft.Data.SqlClient;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 20:05:16