.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
相关产品推荐
相关产品推荐

