Асинхронное подключение к SQLite

Последнее обновление: 14.01.2021

В прошлой теме для подключения к базе данных SQLite мы использовали синхронные методы, которые на время вызова и выполнения операций с данными блокировали основной поток графического приложения. Однако библиотека SQLite.NET предоставляет также асинхронный API. Он предоставляет методы, которые во многом похожи на синхронные.

Асинхронное подключение в SQLite.NET представляет объект класса SQLiteAsyncConnection. А весь асинхронный API раскрывается через методы этого класса:

  • CreateTableAsync(): создание таблицы

  • DropTableAsync(): удаление таблицы

  • Table<T>(): получение данных из таблицы

  • GetAsync<T>(): получение объекта

  • DeleteAsync(): удаление объекта

  • UpdateAsync(): обновление существующего объекта

  • InsertAsync(): добавление нового элемента

  • ExecuteAsync(): выполнение sql-выражения

  • ExecuteScalarAsync(): выполнение sql-выражения и получение результата

  • FindAsync(): поиск объекта в БД

  • QueryAsync(): выполнение запроса SQL

  • InsertAllAsync(): добавление нескольких объектов

  • UpdateAllAsync(): обновление нескольких объектов

Для применения асинхронного API возьмем проект из прошлой темы и добавим в него новый класс FriendsAsyncRepository:

using System.Collections.Generic;
using System.Threading.Tasks;
using SQLite;
using Xamarin.Forms;

namespace HelloApp
{
    public class FriendAsyncRepository
    {
        SQLiteAsyncConnection database;
        
        public FriendAsyncRepository(string databasePath)
        {
            database = new SQLiteAsyncConnection(databasePath);
        }

        public async Task CreateTable()
        {
            await database.CreateTableAsync<Friend>();
        }
        public async Task<List<Friend>> GetItemsAsync()
        {
            return await database.Table<Friend>().ToListAsync();

        }
        public async Task<Friend> GetItemAsync(int id)
        {
            return await database.GetAsync<Friend>(id);
        }
        public async Task<int> DeleteItemAsync(Friend item)
        {
            return await database.DeleteAsync(item);
        }
        public async Task<int> SaveItemAsync(Friend item)
        {
            if (item.Id != 0)
            {
                await database.UpdateAsync(item);
                return item.Id;
            }
            else
            {
                return await database.InsertAsync(item);
            }
        }
    }
}

В отличие от синхронной версии здесь создание таблицы вынесено в отдельный метод.

И далее похожим образом мы сможем использовать этот репозиторий. Установим его в классе App:

using System;
using System.IO;
using Xamarin.Forms;

namespace HelloApp
{
    public partial class App : Application
    {
        public const string DATABASE_NAME = "friends2.db";
        public static FriendAsyncRepository database;
        public static FriendAsyncRepository Database
        {
            get
            {
                if (database == null)
                {
                    database = new FriendAsyncRepository(
                        Path.Combine(
                            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), DATABASE_NAME));
                }
                return database;
            }
        }
        public App()
        {
            InitializeComponent();
            MainPage = new NavigationPage(new MainPage());
        }
        protected override void OnStart()
        {
        }
        protected override void OnSleep()
        {
        }
        protected override void OnResume()
        {
        }
    }
}

И затем используем на главной странице приложения:

using System;
using Xamarin.Forms;

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

		protected override async void OnAppearing()
		{
			// создание таблицы, если ее нет
			await App.Database.CreateTable();
			// привязка данных
			friendsList.ItemsSource = await App.Database.GetItemsAsync();
			
			base.OnAppearing();
		}
		// обработка нажатия элемента в списке
        private async void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
        {
            Friend selectedFriend = (Friend)e.SelectedItem;
            FriendPage friendPage = new FriendPage();
            friendPage.BindingContext = selectedFriend;
            await Navigation.PushAsync(friendPage);
        }
        // обработка нажатия кнопки добавления
        private async void CreateFriend(object sender, EventArgs e)
        {
            Friend friend = new Friend();
            FriendPage friendPage = new FriendPage();
            friendPage.BindingContext = friend;
            await Navigation.PushAsync(friendPage);
        }
	}
}

Xaml-код страницы MainPage выглядит следующим образом:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HelloApp.MainPage" Title="Список друзей">
  <StackLayout>
    <ListView x:Name="friendsList" ItemsSource="{Binding}" ItemSelected="OnItemSelected">
      <ListView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <ViewCell.View>
              <StackLayout Orientation="Horizontal">
                <Label Text="{Binding Name}" FontSize="Medium" />
              </StackLayout>
            </ViewCell.View>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
    <Button Text="Добавить" Clicked="CreateFriend" />
  </StackLayout>
</ContentPage>

При переходе на любую страницу у нее вызывается метод OnAppearing(), поэтому тут мы можем установить привязку и настроить другие начальные данные.

Интерфейс страницы FriendPage для отображения одного объекта:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HelloApp.FriendPage" Title="Информация о друге">
  <StackLayout>
    <Label Text="Имя" />
    <Entry Text="{Binding Name}" />
    <Label Text="Email" />
    <Entry Text="{Binding Email}" />
    <Label Text="Телефон" />
    <Entry Text="{Binding Phone}" />
    <StackLayout Orientation="Horizontal">
      <Button Text="Сохранить" Clicked="SaveFriend" />
      <Button Text="Удалить" Clicked="DeleteFriend" />
      <Button Text="Отмена" Clicked="Cancel" />
    </StackLayout>
  </StackLayout>
</ContentPage>

А в файле связанного кода FriendPage.xaml.cs определим обработчики нажатия кнопок:

using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace HelloApp
{
    public partial class FriendPage : ContentPage
    {
        public FriendPage()
        {
            InitializeComponent();
        }
        private async void SaveFriend(object sender, EventArgs e)
        {
            var friend = (Friend)BindingContext;
            if (!String.IsNullOrEmpty(friend.Name))
            {
                await App.Database.SaveItemAsync(friend);
            }
            await this.Navigation.PopAsync();
        }
        private async void DeleteFriend(object sender, EventArgs e)
        {
            var friend = (Friend)BindingContext;
            await App.Database.DeleteItemAsync(friend);
            await this.Navigation.PopAsync();
        }
        private async void Cancel(object sender, EventArgs e)
        {
            await this.Navigation.PopAsync();
        }
    }
}

ConfigureAwait

Часто данные извлекаются и обрабатываются в фоновом потоке. В этом случае, чтобы после выполнения операции await обработка продолжилась в фоновом потоке, применяется метод ConfigureAwait(false):

public async Task GetItemsAsync()
{
	var friends = await database.Table<Friend>().ToListAsync().ConfigureAwait(false);
	// Операции продолжают выполняться в фоновом потоке, а не в UI-потоке
	for(var friend in friends){ ... }
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850