В прошлой теме мы подключались к базе данных SQLite, которая автоматически создавалась (при ее отсутствии) при запуске приложения. Однако это не всегда бывает удобно. Иногда необходимо, чтобы к первому запуску приложения база данных уже имела некоторые начальные данные, которые мы тут же можем использовать. Поэтому рассмотрим, как мы можем добавить в проект уже имеющуюся базу данных SQLite и использовать ее.
Вначале создадим саму базу данных. Для работы с SQLite установим специальный инструмент - DB Browser for SQLite.
Откроем DB Browser for SQLite и для создания новой базы данных нажмем на кнопку New Database. В открывшемся окне выберем место расположения файла базы данных и укажем для нее имя, например, friends5.
Далее нам будет предложено добавить в базу данных таблицы. Возьмем проект из прошлой темы. В нем была модель Friend:
using SQLite; namespace HelloApp { [Table("Friends")] public class Friend { [PrimaryKey, AutoIncrement, Column("_id")] public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } public string Phone { get; set; } } }
Для хранения объектов данной модели определим в базе данных новую таблицу Users и добавим четыре столбца _id, Name, Email, Phone, как на скриншоте::
Эта таблица имеет столбцы, которые соответствуют свойствам класса Friend.
Можно тут же добавить некоторые начальные данные в таблицу.
Теперь нам надо определить логику передачи файла базы данных в каталог приложения. Это можно сделать различными способами, которые в каких-то ситуациях могут оказаться по своему более оптимальными или менее оптимальными. В данном случае рассмотрим простейший способ.
После создания базы данных добавим ее в главный проект решения и установим в окне Properties (Свойства) у файла базы данных в поле Build Action значение Embedded Resource:
Далее откроем файл App.xaml.cs и определим логику копирования базы данных:
using System; using System.IO; using Xamarin.Forms; using System.Reflection; namespace HelloApp { public partial class App : Application { public const string DATABASE_NAME = "friends5.db"; public static FriendAsyncRepository database; public static FriendAsyncRepository Database { get { if (database == null) { // путь, по которому будет находиться база данных string dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), DATABASE_NAME); // если база данных не существует (еще не скопирована) if (!File.Exists(dbPath)) { // получаем текущую сборку var assembly = IntrospectionExtensions.GetTypeInfo(typeof(App)).Assembly; // берем из нее ресурс базы данных и создаем из него поток using (Stream stream = assembly.GetManifestResourceStream($"HelloApp.{DATABASE_NAME}")) { using (FileStream fs = new FileStream(dbPath, FileMode.OpenOrCreate)) { stream.CopyTo(fs); // копируем файл базы данных в нужное нам место fs.Flush(); } } } database = new FriendAsyncRepository(dbPath); } return database; } } public App() { InitializeComponent(); MainPage = new NavigationPage(new MainPage()); } protected override void OnStart() { } protected override void OnSleep() { } protected override void OnResume() { } } }
В данном случае наша задача скопировать файл базы данных в папку . Для этого вначале получаем встроенный ресурс посредством метода
assembly.GetManifestResourceStream($"HelloApp.{DATABASE_NAME}"))
. "HelloApp" в данном случае это название текущей сборки -
по сути название проекта, соответственно если название проекта другое, то оно может изменяться.
Далее данные из полученного потока с помощью объекта FileStream сохраняем в нужное нам место.
При этом нам НЕ надо ничего добавлять в другие проекты или изменять их код.
Таким образом, при запуске приложения и обращения к базе данных произойдет копирование файла в нужное место и затем новосозданный файл будет использоваться для работы с приложением.
Весь остальной код в данном случае остается тем же, что и вв прошлой теме. В частности, код репозитория - класса FriendAsyncRepository:
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); } } } }
Xaml-код страницы MainPage.xaml выглядит следующим образом:
<?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>
Код MainPage.xaml.cs:
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); } } }
Интерфейс страницы FriendPage.xaml для отображения одного объекта:
<?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(); } } }