Quantcast
Channel: かずきのBlog@hatena
Viewing all articles
Browse latest Browse all 1387

Xamarin.Forms + PrismでSQLiteを使ってみよう

$
0
0

モバイル環境でのデータベースといったらSQLiteがデファクト!ということでXamarin.Forms + Prism.Formsの環境で試してみましょう。

NuGetパッケージの導入

使用するパッケージはSQLite-net-pclです。(似た名前のが多いので注意)

www.nuget.org

プラットフォーム固有処理を作成

残念なことにPCLに閉じて完結という感じではなさそうです。 コネクションの作成時にパスを渡すのですが、このパスがプラットフォーム固有文字列になるので以下のようにSQLiteConnectionを返すインターフェースを定義していい感じにやる必要があります。

using SQLite;

namespace PrismUnityApp17.Services
{
    publicinterface ISQLiteConnectionProvider
    {
        SQLiteConnection GetConnection();
    }
}

Androidの実装

Personalフォルダをとってきて、そこにファイルを作る感じにします。

using PrismUnityApp17.Services;
using SQLite;
using System.IO;

namespace PrismUnityApp17.Droid.Services
{
    publicclass SQLiteConnectionProvider : ISQLiteConnectionProvider
    {
        private SQLiteConnection Connection { get; set; }

        public SQLiteConnection GetConnection()
        {
            if (this.Connection != null) { returnthis.Connection; }

            var path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
            path = Path.Combine(path, "database.db3");
            returnthis.Connection = new SQLiteConnection(path);
        }
    }
}

iOSの実装

iOSはLibraryフォルダに作る感じです。(ちょっとAndroidに比べてめんどい)

using PrismUnityApp17.Services;
using SQLite;
using System;
using System.IO;

namespace PrismUnityApp17.iOS.Services
{
    publicclass SQLiteConnectionProvider : ISQLiteConnectionProvider
    {
        private SQLiteConnection Connection { get; set; }

        public SQLiteConnection GetConnection()
        {
            if (this.Connection != null) { returnthis.Connection; }

            var path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
            path = Path.Combine(path, "..", "Library", "database.db3");
            returnthis.Connection = new SQLiteConnection(path);
        }
    }
}

悩み

SQLiteConnectionIDispoasbleなのでDisposeしないとなのですが、毎回やるのとstaticに持ってて、使いまわすのどっちが正解なのか悩んでます…。とりあえず今回の例ではアプリ内で1つのコネクションにしてます。

PlatformInitializerへの登録

上記で作成したクラスをIPlatformInitializerで登録します。

Android

MainActivityの下に定義されてるので以下のように追加します。

publicclass AndroidInitializer : IPlatformInitializer
{
    publicvoid RegisterTypes(IUnityContainer container)
    {
        container.RegisterType<ISQLiteConnectionProvider, SQLiteConnectionProvider>(new ContainerControlledLifetimeManager());
    }
}

iOS

iOSはAppDelegateの下に定義されているので、そこにも追加します。

publicclass iOSInitializer : IPlatformInitializer
{
    publicvoid RegisterTypes(IUnityContainer container)
    {
        container.RegisterType<ISQLiteConnectionProvider, SQLiteConnectionProvider>(new ContainerControlledLifetimeManager());
    }
}

テーブルの定義

こんな感じでテーブルを表すクラスを定義します。

using SQLite;

namespace PrismUnityApp17.Businesses
{
    publicclass TodoItem
    {
        [PrimaryKey]
        [AutoIncrement]
        publicint Id { get; set; }
        [NotNull]
        publicstring Title { get; set; }
    }
}

そして、テーブルにアクセスするためのクラスを作ります。

using PrismUnityApp17.Businesses;
using SQLite;
using System.Collections.Generic;
using System.Linq;

namespace PrismUnityApp17.Services
{
    publicinterface ITodoItemService
    {
        IEnumerable<TodoItem> GetAll();
        TodoItem GetById(int id);
        void Update(TodoItem todoItem);
        void Insert(TodoItem todoItem);
        void Delete(int id);
    }

    publicclass TodoItemService : ITodoItemService
    {
        private ISQLiteConnectionProvider ConnectionProvider { get; }
        private SQLiteConnection Connection { get; }

        public TodoItemService(ISQLiteConnectionProvider connectionProvider)
        {
            this.ConnectionProvider = connectionProvider;
            this.Connection = this.ConnectionProvider.GetConnection();
            this.Connection.CreateTable<TodoItem>();
        }

        publicvoid Delete(int id)
        {
            this.Connection.Delete<TodoItem>(id);
        }

        public IEnumerable<TodoItem> GetAll()
        {
            returnthis.Connection.Table<TodoItem>().ToList();
        }

        public TodoItem GetById(int id)
        {
            returnthis.Connection.Table<TodoItem>().FirstOrDefault(x => x.Id == id);
        }

        publicvoid Insert(TodoItem todoItem)
        {
            this.Connection.Insert(todoItem);
        }

        publicvoid Update(TodoItem todoItem)
        {
            this.Connection.Update(todoItem);
        }
    }
}

画面とVMを作ろう

あとは、これを使う画面を作るだけです。とりあえず追加と削除を。

using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using PrismUnityApp17.Businesses;
using PrismUnityApp17.Services;
using System.Collections.Generic;

namespace PrismUnityApp17.ViewModels
{
    publicclass MainPageViewModel : BindableBase, INavigationAware
    {
        private ITodoItemService TodoItemService { get; }

        private IEnumerable<TodoItem> todoItems;

        public IEnumerable<TodoItem> TodoItems
        {
            get { returnthis.todoItems; }
            set { this.SetProperty(refthis.todoItems, value); }
        }

        privatestring inputText;

        publicstring InputText
        {
            get { returnthis.inputText; }
            set { this.SetProperty(refthis.inputText, value); }
        }

        public DelegateCommand AddCommand { get; }

        public DelegateCommand<TodoItem> DeleteCommand { get; }

        public MainPageViewModel(ITodoItemService todoItemService)
        {
            this.TodoItemService = todoItemService;

            this.AddCommand = new DelegateCommand(this.AddTodoItem, () => !string.IsNullOrEmpty(this.InputText))
                .ObservesProperty(() => this.InputText);

            this.DeleteCommand = new DelegateCommand<TodoItem>(this.DeleteTodoItem);
        }

        privatevoid DeleteTodoItem(TodoItem todoItem)
        {
            this.TodoItemService.Delete(todoItem.Id);
            this.TodoItems = this.TodoItemService.GetAll();
        }

        privatevoid AddTodoItem()
        {
            this.TodoItemService.Insert(new TodoItem { Title = this.InputText });
            this.InputText = "";
            this.TodoItems = this.TodoItemService.GetAll();
        }

        publicvoid OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        publicvoid OnNavigatedTo(NavigationParameters parameters)
        {
            this.TodoItems = this.TodoItemService.GetAll();
        }
    }
}

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"xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"prism:ViewModelLocator.AutowireViewModel="True"x:Class="PrismUnityApp17.Views.MainPage"Title="MainPage"x:Name="Page"><ContentPage.Padding><OnPlatform x:TypeArguments="Thickness"iOS="0,20,0,0" /></ContentPage.Padding><ContentPage.ToolbarItems><ToolbarItem Text="Add"Command="{Binding AddCommand}" /></ContentPage.ToolbarItems><StackLayout><Entry Text="{Binding InputText, Mode=TwoWay}" /><ListView ItemsSource="{Binding TodoItems}"VerticalOptions="FillAndExpand"><ListView.ItemTemplate><DataTemplate><ViewCell><ViewCell.ContextActions><MenuItem Text="Delete"Command="{Binding BindingContext.DeleteCommand, Source={x:Reference Page}}"CommandParameter="{Binding}" /></ViewCell.ContextActions><Label Text="{Binding Title}" /></ViewCell></DataTemplate></ListView.ItemTemplate></ListView></StackLayout></ContentPage>

仕上げにApp.xaml

ITodoItemServiceの登録や、ページの登録(NavigationPageの登録など)をやります。

using Microsoft.Practices.Unity;
using Prism.Unity;
using PrismUnityApp17.Services;
using PrismUnityApp17.Views;
using Xamarin.Forms;

namespace PrismUnityApp17
{
    publicpartialclass App : PrismApplication
    {
        public App(IPlatformInitializer initializer = null) : base(initializer) { }

        protectedoverride async void OnInitialized()
        {
            InitializeComponent();

            await this.NavigationService.NavigateAsync("NavigationPage/MainPage");
        }

        protectedoverridevoid RegisterTypes()
        {
            this.Container.RegisterTypeForNavigation<MainPage>();
            this.Container.RegisterTypeForNavigation<NavigationPage>();

            this.Container.RegisterType<ITodoItemService, TodoItemService>(new ContainerControlledLifetimeManager());
        }
    }
}

これで、追加と削除ができるアプリが出来上がり。意外とお手軽ですね。

Async対応

Async対応版に改造してみます。

ISQLiteConnectionProviderを改造

SQLiteAsyncConnectionを返すようにします。

using SQLite;

namespace PrismUnityApp17.Services
{
    publicinterface ISQLiteConnectionProvider
    {
        SQLiteAsyncConnection GetConnection();
    }
}

プラットフォーム固有実装でもSQLiteAsyncConnectionを返すようにします。

Android

using PrismUnityApp17.Services;
using SQLite;
using System.IO;

namespace PrismUnityApp17.Droid.Services
{
    publicclass SQLiteConnectionProvider : ISQLiteConnectionProvider
    {
        private SQLiteAsyncConnection Connection { get; set; }

        public SQLiteAsyncConnection GetConnection()
        {
            if (this.Connection != null) { returnthis.Connection; }

            var path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
            path = Path.Combine(path, "database.db3");
            returnthis.Connection = new SQLiteAsyncConnection(path);
        }
    }
}

iOS

using PrismUnityApp17.Services;
using SQLite;
using System;
using System.IO;

namespace PrismUnityApp17.iOS.Services
{
    publicclass SQLiteConnectionProvider : ISQLiteConnectionProvider
    {
        private SQLiteAsyncConnection Connection { get; set; }

        public SQLiteAsyncConnection GetConnection()
        {
            if (this.Connection != null) { returnthis.Connection; }

            var path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
            path = Path.Combine(path, "..", "Library", "database.db3");
            returnthis.Connection = new SQLiteAsyncConnection(path);
        }
    }
}

TodoItemServiceの非同期化

TodoItemServiceを非同期に書き換えます。

using PrismUnityApp17.Businesses;
using SQLite;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace PrismUnityApp17.Services
{
    publicinterface ITodoItemService
    {
        Task<IEnumerable<TodoItem>> GetAllAsync();
        Task<TodoItem> GetByIdAsync(int id);
        Task UpdateAsync(TodoItem todoItem);
        Task InsertAsync(TodoItem todoItem);
        Task DeleteAsync(TodoItem todoItem);
    }

    publicclass TodoItemService : ITodoItemService
    {
        private ISQLiteConnectionProvider ConnectionProvider { get; }
        private SQLiteAsyncConnection Connection { get; }

        public TodoItemService(ISQLiteConnectionProvider connectionProvider)
        {
            this.ConnectionProvider = connectionProvider;
            this.Connection = this.ConnectionProvider.GetConnection();
        }

        public async Task DeleteAsync(TodoItem todoItem)
        {
            await this.Connection.CreateTableAsync<TodoItem>();
            await this.Connection.DeleteAsync(todoItem);
        }

        public async Task<IEnumerable<TodoItem>> GetAllAsync()
        {
            await this.Connection.CreateTableAsync<TodoItem>();
            return await this.Connection.Table<TodoItem>().ToListAsync();
        }

        public async Task<TodoItem> GetByIdAsync(int id)
        {
            await this.Connection.CreateTableAsync<TodoItem>();
            return await this.Connection.Table<TodoItem>().Where(x => x.Id == id).FirstOrDefaultAsync();
        }

        public async Task InsertAsync(TodoItem todoItem)
        {
            await this.Connection.CreateTableAsync<TodoItem>();
            await this.Connection.InsertAsync(todoItem);
        }

        public async Task UpdateAsync(TodoItem todoItem)
        {
            await this.Connection.CreateTableAsync<TodoItem>();
            await this.Connection.UpdateAsync(todoItem);
        }
    }
}

ViewModelの非同期対応

最後にViewModelを非同期対応にします。

using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using PrismUnityApp17.Businesses;
using PrismUnityApp17.Services;
using System.Collections.Generic;

namespace PrismUnityApp17.ViewModels
{
    publicclass MainPageViewModel : BindableBase, INavigationAware
    {
        private ITodoItemService TodoItemService { get; }

        private IEnumerable<TodoItem> todoItems;

        public IEnumerable<TodoItem> TodoItems
        {
            get { returnthis.todoItems; }
            set { this.SetProperty(refthis.todoItems, value); }
        }

        privatestring inputText;

        publicstring InputText
        {
            get { returnthis.inputText; }
            set { this.SetProperty(refthis.inputText, value); }
        }

        public DelegateCommand AddCommand { get; }

        public DelegateCommand<TodoItem> DeleteCommand { get; }

        public MainPageViewModel(ITodoItemService todoItemService)
        {
            this.TodoItemService = todoItemService;

            this.AddCommand = new DelegateCommand(this.AddTodoItem, () => !string.IsNullOrEmpty(this.InputText))
                .ObservesProperty(() => this.InputText);

            this.DeleteCommand = new DelegateCommand<TodoItem>(this.DeleteTodoItem);
        }

        private async void DeleteTodoItem(TodoItem todoItem)
        {
            await this.TodoItemService.DeleteAsync(todoItem);
            this.TodoItems = await this.TodoItemService.GetAllAsync();
        }

        private async void AddTodoItem()
        {
            await this.TodoItemService.InsertAsync(new TodoItem { Title = this.InputText });
            this.InputText = "";
            this.TodoItems = await this.TodoItemService.GetAllAsync();
        }

        publicvoid OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public async void OnNavigatedTo(NavigationParameters parameters)
        {
            this.TodoItems = await this.TodoItemService.GetAllAsync();
        }
    }
}

これでばっちり!非同期でも動きますね。


Viewing all articles
Browse latest Browse all 1387

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>