无法访问 MvvmCross ViewModel 中的 SQLite 数据
Unable to Access SQLite Data in MvvmCross ViewModel
你好 Whosebug 社区,
我知道这个 post 中有很多代码,但我想给你们,社区尽可能多地了解这里发生了什么,这样也许有人可以提供帮助我弄清楚我的问题是什么。
最近,对于我正在进行的项目,我们决定从 MvvmCross 5.7.0 升级到 6.2.2。我已经设法让我们的 UWP 应用程序成功完成初始化和设置过程。我们注册应用程序启动的第一个视图模型也开始初始化。但是,我发现我的 vm 初始化挂在特定的代码行(如下面的代码所示)。最奇怪的部分是在应用程序初始化代码 运行 中调用的类似方法,没有 hanging/deadlock 就完全没问题,所以我不确定有什么不同这里是我的视图模型代码的简化版本来说明:
public class MyViewModel : BaseAuthenticatedTabBarViewModel, IMvxViewModel<int>
{
private int? _settingValue;
public override async Task Initialize()
{
//Some irrelevant initialization code
Exception e = null;
try
{
//This line of code never returns
_settingValue = _settingValue ?? await AppSettingService.GetSettingValue();
}
catch (Exception ex)
{
e = ex;
}
if (e != null)
{
await HandleCatastrophicError(e);
}
}
}
AppSettingService.GetSettingValue() 方法如下所示:
public async Task<int?> GetCurrentEventId()
{
return await GetNullableIntSetting("SettingValue");
}
private static async Task<int?> GetNullableIntSetting(string key)
{
try
{
var setting = await SettingDataService.SettingByName(key);
if (setting != null)
{
return string.IsNullOrEmpty(setting.Value) ? (int?)null : Convert.ToInt32(setting.Value);
}
}
catch (Exception ex)
{
//Handle the exception
}
return null;
}
SettingDataService的所有代码:
public class SettingDataService : DataService<SettingDataModel>, ISettingDataService
{
public async Task<SettingDataModel> SettingByName(string name)
{
try
{
var values = (await WhereAsync(e => e.Name == name));
return values.FirstOrDefault();
}
catch(Exception ex)
{
//Handle the exception
}
return null;
}
}
最后,WhereAsync() 的实现在一个名为 DataService 的 class 中,如下所示:
public virtual async Task<IEnumerable<T>> WhereAsync(System.Linq.Expressions.Expression<Func<T, bool>> condition, SQLiteAsyncConnection connection = null)
{
return await (connection ?? await GetConnectionAsync())
.Table<T>()
.Where(condition)
.ToListAsync();
}
非常感谢您的提前帮助
编辑:忘记添加这段关键的代码来进一步帮助你们:
protected async Task<SQLiteAsyncConnection> GetConnectionAsync()
{
SQLiteAsyncConnection connection = null;
while (true)
{
try
{
connection = Factory.Create(App.DatabaseName);
// This line of code is the culprit. For some reason this hangs and I can't figure out why.
await connection.CreateTableAsync<T>();
break;
}
catch (SQLiteException ex)
{
if (ex.Result != Result.CannotOpen && ex.Result != Result.Busy && ex.Result != Result.Locked)
{
throw;
}
}
await Task.Delay(20);
}
return connection;
}
我怀疑您正在调用 Task.Wait
或 Task<T>.Result
调用堆栈中更远的地方。或者,如果您不这样做,MvvmCross is probably doing it for you. This will cause a deadlock 从 UI 上下文调用时。
就我个人而言,我更喜欢 ViewModel 应该始终同步构建的方法,并且不能有异步 "initialization"。也就是说,它们必须(同步地)将自己构造成 "loading" 状态,并且这种构造可以启动异步操作,稍后将 更新 它们进入 "loaded"状态。同步初始化模式意味着在更改视图时永远不会有不必要的延迟;您的用户可能只会看到微调器或加载消息,但至少他们会看到一些东西。请参阅我关于 async MVVM data binding for a pattern that helps with this, and note that there's a newer version of the helper types in that article 的文章。
你好 Whosebug 社区,
我知道这个 post 中有很多代码,但我想给你们,社区尽可能多地了解这里发生了什么,这样也许有人可以提供帮助我弄清楚我的问题是什么。
最近,对于我正在进行的项目,我们决定从 MvvmCross 5.7.0 升级到 6.2.2。我已经设法让我们的 UWP 应用程序成功完成初始化和设置过程。我们注册应用程序启动的第一个视图模型也开始初始化。但是,我发现我的 vm 初始化挂在特定的代码行(如下面的代码所示)。最奇怪的部分是在应用程序初始化代码 运行 中调用的类似方法,没有 hanging/deadlock 就完全没问题,所以我不确定有什么不同这里是我的视图模型代码的简化版本来说明:
public class MyViewModel : BaseAuthenticatedTabBarViewModel, IMvxViewModel<int>
{
private int? _settingValue;
public override async Task Initialize()
{
//Some irrelevant initialization code
Exception e = null;
try
{
//This line of code never returns
_settingValue = _settingValue ?? await AppSettingService.GetSettingValue();
}
catch (Exception ex)
{
e = ex;
}
if (e != null)
{
await HandleCatastrophicError(e);
}
}
}
AppSettingService.GetSettingValue() 方法如下所示:
public async Task<int?> GetCurrentEventId()
{
return await GetNullableIntSetting("SettingValue");
}
private static async Task<int?> GetNullableIntSetting(string key)
{
try
{
var setting = await SettingDataService.SettingByName(key);
if (setting != null)
{
return string.IsNullOrEmpty(setting.Value) ? (int?)null : Convert.ToInt32(setting.Value);
}
}
catch (Exception ex)
{
//Handle the exception
}
return null;
}
SettingDataService的所有代码:
public class SettingDataService : DataService<SettingDataModel>, ISettingDataService
{
public async Task<SettingDataModel> SettingByName(string name)
{
try
{
var values = (await WhereAsync(e => e.Name == name));
return values.FirstOrDefault();
}
catch(Exception ex)
{
//Handle the exception
}
return null;
}
}
最后,WhereAsync() 的实现在一个名为 DataService 的 class 中,如下所示:
public virtual async Task<IEnumerable<T>> WhereAsync(System.Linq.Expressions.Expression<Func<T, bool>> condition, SQLiteAsyncConnection connection = null)
{
return await (connection ?? await GetConnectionAsync())
.Table<T>()
.Where(condition)
.ToListAsync();
}
非常感谢您的提前帮助
编辑:忘记添加这段关键的代码来进一步帮助你们:
protected async Task<SQLiteAsyncConnection> GetConnectionAsync()
{
SQLiteAsyncConnection connection = null;
while (true)
{
try
{
connection = Factory.Create(App.DatabaseName);
// This line of code is the culprit. For some reason this hangs and I can't figure out why.
await connection.CreateTableAsync<T>();
break;
}
catch (SQLiteException ex)
{
if (ex.Result != Result.CannotOpen && ex.Result != Result.Busy && ex.Result != Result.Locked)
{
throw;
}
}
await Task.Delay(20);
}
return connection;
}
我怀疑您正在调用 Task.Wait
或 Task<T>.Result
调用堆栈中更远的地方。或者,如果您不这样做,MvvmCross is probably doing it for you. This will cause a deadlock 从 UI 上下文调用时。
就我个人而言,我更喜欢 ViewModel 应该始终同步构建的方法,并且不能有异步 "initialization"。也就是说,它们必须(同步地)将自己构造成 "loading" 状态,并且这种构造可以启动异步操作,稍后将 更新 它们进入 "loaded"状态。同步初始化模式意味着在更改视图时永远不会有不必要的延迟;您的用户可能只会看到微调器或加载消息,但至少他们会看到一些东西。请参阅我关于 async MVVM data binding for a pattern that helps with this, and note that there's a newer version of the helper types in that article 的文章。