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

.NET MAUI应用执行CreateTableAsync时卡顿无响应求助

.NET MAUI集成SQLite-net时CreateTableAsync导致应用卡顿无响应

我按照教程在.NET MAUI应用中集成SQLite-net,但执行await db.CreateTableAsync<Restroom>();时,应用直接卡顿无响应。

已经尝试的方案:

  • 用ConfigureAwait(false)避免死锁
  • 用MainThread.BeginInvokeOnMainThread包裹调用
  • 更新所有依赖项
  • 实现raw.bundle.green
  • 创建全新项目复现(问题依旧)
  • 移除Restroom模型的Location属性并调整代码
  • 在.csproj中添加<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>关闭Fast Deploy

相关代码

Restroom.cs

using SQLite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Dook.Model
{
    public class Restroom
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string Username { get; set; }
        public Location PinLocation { get; set; }
    }
}

RestroomService.cs

using Dook.Model;
using SQLite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Dook.Services
{
    public static class RestroomService
    {
        static SQLiteAsyncConnection db;

        static async Task InitAsync()
        {
            if(db != null) 
                return;

            //获取数据库文件的绝对路径
            var databasePath = Path.Combine(FileSystem.AppDataDirectory, "MyData.db");

            db = new SQLiteAsyncConnection(databasePath);

            await db.CreateTableAsync<Restroom>();
        }

        public static async Task AddPinAsync(string name, string address, string username, Location location)
        {
            await InitAsync();
            var test = "test";
            var restroom = new Restroom
            {
                Name = name,
                Address = address,
                Username = username,
                PinLocation = location
            };

            var id = await db.InsertAsync(restroom);
        }

        public static async Task RemovePinAsync(int id)
        {
            await InitAsync();

            await db.DeleteAsync<Restroom>(id);
        }

        public static async Task<IEnumerable<Restroom>> GetPinAsync()
        {
            await InitAsync();

            var restroom = await db.Table<Restroom>().ToListAsync();
            return restroom;
        }
    }
}

MainViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Maui.Controls.Maps;
using Microsoft.Maui.Maps;
using Map = Microsoft.Maui.Controls.Maps.Map;
using Dook.Model;
using Dook.Services;
using MvvmHelpers;
using MvvmHelpers.Commands;
using CommunityToolkit.Maui.Core.Extensions;

namespace Dook.ViewModel
{
    public partial class MainViewModel : BaseViewModel
    {
        public ObservableRangeCollection<Restroom> Restroom { get; set; }
        public AsyncCommand RefreshCommand { get; }
        public AsyncCommand<Location> AddCommand { get; }
        public AsyncCommand<Restroom> RemoveCommand { get; }

        public MainViewModel()
        {
            Title = "Map Controller";

            Restroom = new ObservableRangeCollection<Restroom>();

            AddCommand = new AsyncCommand<Location>(Add);
            RemoveCommand = new AsyncCommand<Restroom>(Remove);
            RefreshCommand = new AsyncCommand(Refresh);
        }

        async Task Add(Location pinlocation)
        {
            var name = await App.Current.MainPage.DisplayPromptAsync("Location Name", "Name of Location");
            var address = "test";
            var username = await App.Current.MainPage.DisplayPromptAsync("Username", "Username of Toilet Adder");
            Location location = pinlocation;
            if(name == null || address == null || username == null) { return; }
            await RestroomService.AddPinAsync(name, address, username, location);
            await Refresh();
        }
        
        async Task Remove(Restroom restroom)
        {
            await RestroomService.RemovePinAsync(restroom.Id);
            await Refresh();
        }

        async Task Refresh()
        {
            IsBusy = true;
            await Task.Delay(2000);
            Restroom.Clear();
            var restrooms = await RestroomService.GetPinAsync();
            Restroom.AddRange(restrooms);
            IsBusy = false;
        }

        public static Location GetLocation()
        {
            try
            {
                Location location = new();
                location = Geolocation.Default.GetLastKnownLocationAsync().Result;
                if (location != null)
                    return location;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Unable to get location: {ex.Message}");
                Application.Current.MainPage.DisplayAlert("Error!", ex.Message, "OK");
            }

            return null;
        }
    }
}

MainPage.xaml.cs

namespace Dook;
using Microsoft.Maui.Controls.Maps;
using Microsoft.Maui.Maps;
using System.Diagnostics;
using Map = Microsoft.Maui.Controls.Maps.Map;
using Dook.ViewModel;
using Dook.Model;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        MoveMapLocation();
    }

    private void GoToLocation_Button(object sender, EventArgs e)
    {
        MoveMapLocation();
    }

    private void OnMapClicked(object sender, MapClickedEventArgs e)
    {
        var viewModel = new MainViewModel();

        if(viewModel.AddCommand.CanExecute(e.Location)) 
            viewModel.AddCommand.ExecuteAsync(e.Location);
    }

    private void RefreshButton_Clicked(object sender, EventArgs e)
    {
        RefreshPins();
    }

    private void MoveMapLocation()
    {
        //避免重复代码的工具函数
        MapSpan mapSpan = new MapSpan(MainViewModel.GetLocation(), 0.01, 0.01);
        mainmap.MoveToRegion(mapSpan);
    }

    private void RefreshPins()
    {
        var viewModel = new MainViewModel();

        viewModel.RefreshCommand.ExecuteAsync();
    }
}

并行任务窗口截图

并行任务窗口


问题根源与解决方案

核心问题:SQLite-net不支持Location类型序列化

你的Restroom模型中直接使用了Location复杂类型,SQLite-net默认无法识别并序列化这种非基础类型,导致CreateTableAsync执行时内部卡住,进而阻塞整个应用主线程。

具体解决办法

办法1:拆分Location为基础字段存储

把Location的经纬度等属性拆成单独的基础类型字段,SQLite-net原生支持数值类型存储:

public class Restroom
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string Username { get; set; }
    
    // 将Location拆分为可存储的基础字段
    public double Latitude { get; set; }
    public double Longitude { get; set; }
    public double? Altitude { get; set; }
}

业务代码中,将Location对象转换为这些字段存储;查询时再重新组装成Location对象即可。

办法2:为Location添加自定义序列化逻辑

用JSON序列化把Location转成字符串存储,需要引入Newtonsoft.Json或System.Text.Json NuGet包:

using Newtonsoft.Json;

public class Restroom
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string Username { get; set; }
    
    [Ignore] // 不直接存储该属性
    public Location PinLocation { get; set; }
    
    // 存储序列化后的JSON字符串
    public string PinLocationJson
    {
        get => JsonConvert.SerializeObject(PinLocation);
        set => PinLocation = JsonConvert.DeserializeObject<Location>(value);
    }
}
办法3:修复主线程死锁风险

GetLocation方法中使用.Result同步阻塞异步调用,可能加剧线程死锁问题,改为异步实现:

public static async Task<Location> GetLocationAsync()
{
    try
    {
        var location = await Geolocation.Default.GetLastKnownLocationAsync();
        return location;
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"无法获取位置: {ex.Message}");
        await Application.Current.MainPage.DisplayAlert("错误!", ex.Message, "确定");
    }
    return null;
}

然后在MoveMapLocation中异步调用:

private async void MoveMapLocation()
{
    var location = await MainViewModel.GetLocationAsync();
    if (location != null)
    {
        MapSpan mapSpan = new MapSpan(location, 0.01, 0.01);
        mainmap.MoveToRegion(mapSpan);
    }
}

额外建议

  • 开启SQLite-net的日志功能,查看CreateTableAsync执行时的具体错误:
    db.Trace = true;
    
  • 所有异步操作必须用await调用,禁止使用.Result或.Wait(),避免线程死锁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 07:10:55