【问题标题】:Best practice to call database async methods without blocking ui UWP在不阻塞 ui UWP 的情况下调用数据库异步方法的最佳实践
【发布时间】:2018-08-22 19:56:22
【问题描述】:

我有一个从 SQLite 数据库获取一些数据的应用程序。例如,我有一个类似下面的方法:

public async Task<IEnumerable<TaskItem>> GetAllAsync()
{
    using (var context = new NotesContext())
    {
        try
        {
            return await context
                .Tasks
                .ToListAsync();
        }
        catch (Exception)
        {
            return Enumerable.Empty<TaskItem>();
        }
    }
}

我从页面加载时绑定的命令调用此方法:

LoadedCommand = new RelayCommand<TaskItemViewModel>
            ((task) => GetAllTasksAsync(task));

以及命令调用的方法:

public async void GetAllTasksAsync(TaskListItemViewModel taskList)
{
    ShowTaskListViewProgressRing = true;

    Tasks.Clear();
    TaskAutoSuggestBoxItems.Clear();

    var response = await _dataService
        .TaskService
        .GetAllAsync()
        .ConfigureAwait(false);

    var tasks = _mapper.Map<List<TaskItemViewModel>>(response);
    if (tasks.Count > 0)
    {
        var mainTasks = tasks
            .Where(t => t.ParentTask == null);
        mainTasks.ForEach(t =>
        {
            if (!tasks.Any(st => st.ParentTask == t.TaskID))
                return;
            t.SubTasks = new ObservableCollection<TaskItemViewModel>(
                tasks
                .Where(st => st.ParentTask == t.TaskID)
                .OrderBy(st => st.Position));
        });
        Tasks.AddRange(mainTasks);
        TaskAutoSuggestBoxItems
            .AddRange(_mapper.Map<IEnumerable<ItemModel>>(mainTasks.OrderBy(t => t.Title)));
    }
    CurrentTaskList = taskList;
    ShowTaskListViewProgressRing = false;
}

如果我没记错的话,GetAllTasksAsync 会被 UI 线程调用,但是当它到达 GetAllAsync().ConfigureAwait(false) 时,它会退出其上下文,然后 GetAllAsync 在线程池上下文中运行,当它完成时它会退出返回 UI 线程并更新视图属性。

这种方法正确吗?

【问题讨论】:

  • TaskAutoSuggestBoxItems 是 UI 控件吗?
  • @JohanP 是绑定到 AutoSuggestBox 的 ItemsSourceObservableCollectionproperty
  • 如果您不触摸 GetAllTasksAsync 内的任何 UI 控件,那么使用 configureAwait(false); 就可以了,否则您将由于上下文未被捕获而导致异常,并且继续将在线程池线程。
  • @JohanP by ui 控件在此上下文中是否可以绑定属性?如果那是正确的TasksTaskAutoSuggestBoxItems 绑定到视图,那么应用程序应该崩溃吧?
  • 源属性与 UI target 属性不同。您不能从后台线程触摸控件,但可以设置视图模型的源属性。

标签: c# .net uwp async-await


【解决方案1】:

这种方法正确吗?

捕获异常并假装没有发生这样的事情不是最佳做法:

catch (Exception)
{
    return Enumerable.Empty<TaskItem>();
}

如果GetAllAsync() 无法以有意义的方式处理异常,您不妨避免捕获任何异常并让调用者处理此问题。或者你可能想记录一个错误然后重新抛出异常:

public async Task<IEnumerable<TaskItem>> GetAllAsync()
{
    using (var context = new NotesContext())
    {
        try
        {
            return await context
                .Tasks
                .ToListAsync();
        }
        catch (Exception ex)
        {
            //log ex...
            throw;
        }
    }
}

此外,不返回值的异步方法(例如 GetAllTasksAsync)应该返回 Task 而不是 void

public async Task GetAllTasksAsync(TaskListItemViewModel taskList){ ... }

否则当你调用你应该调用的时候你不能等待它。

您关于捕获SynchronizationContextConfigureAwait 的假设是正确的。

【讨论】:

  • 嗨,谢谢你的回答,是的,我知道当前的 try catch 不是最好的..,把它放在一边,所以如果GetAllTasksAsync 应该返回一个任务,那么我的命令看起来像@ 987654331@ 在这种情况下,我为什么要等待 GetAllTask​​sAsync?。根据@JohanP 的评论,因为我在GetAllAsync()ConfigureAwait(false) 之后修改了 ui 的属性,所以应用程序应该崩溃,但在我的情况下它不会
  • 为什么设置视图模型的源属性会使应用程序崩溃?
  • 而且 RelayCommand 不是异步的。但任何异步方法仍应返回一个任务。
  • 好的,当我调用var response = await _dataService.TaskService.GetAllAsync().ConfigureAwait(false); GetAllAsync() 的内容还是被ui线程调用的吧?那么我应该在return await context .Tasks .ToListAsync(); 中添加ConfigureAwait(false) 还是应该将方法的内容包含在Task.Run() 中?
  • GetAllAsync() 在调用线程上同步执行,直到遇到等待。 async/await 不会导致创建任何新线程。如果您有其他问题,请提出一个新问题。不要在 cmets 字段中问几个不同的问题。
猜你喜欢
  • 2012-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-14
  • 2020-01-26
相关资源
最近更新 更多