shuhelohelo’s blog

Xamarin.Forms多めです.

Xamarin.FormsでEntityFramework CoreとSQLiteを使うチュートリアル メモ

www.youtube.com

github.com

空のXamarin.Formsアプリを作成

f:id:shuhelohelo:20191231112243p:plain

f:id:shuhelohelo:20191231112400p:plain

Sqliteを使ってDBを操作するためのクラスを作成

.net standardクラスライブラリとして作成する.

EntityFramework Coreはクロスプラットフォームで使えるため.

f:id:shuhelohelo:20191231111908p:plain

f:id:shuhelohelo:20191231112537p:plain

Microsoft.EntityFrameworkCore.Sqliteをインストールする

Nugetでインストールする.

f:id:shuhelohelo:20191231112941p:plain

インストール先のプロジェクトはAndroid,iOS,SqliteApp.Standardの3つ.

共通コードにはインストールしないんだ?

Modelを作る

共通コードにモデルを追加する.

    public class Product
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public decimal  Price { get; set; }
        public override string ToString()
        {
            return $"({Id}) {Title}, {Price}";
        }
    }

リポジトリのインターフェースを作成する

共通コードにインターフェースを追加する.

    public interface IProductRepository
    {
        Task<IEnumerable<Product>> GetProductAsync();

        Task<Product> GetProductByIdAsync(int id);
 
        Task<bool> AddProductAsync(Product product);
        
        Task<bool> UpdateProductAsync(Product product);
        
        Task<bool> RemoveProductAsync(int id);
        
        Task<IEnumerable<Product>> QueryProductsAsync(Func<Product,bool> predicate);
    }

リポジトリクラスをクラスライブラリ側に作成する

IProductRepositoryインターフェースを利用するために共通コードのプロジェクトを参照する必要がある.

f:id:shuhelohelo:20191231115446p:plain

f:id:shuhelohelo:20191231115550p:plain

実装は以下のとおり.

    public class ProductRepository : IProductRepository
    {
        private readonly DataBaseContext _databaseContext;

        public ProductRepository(string dbPath)
        {
            _databaseContext = new DataBaseContext(dbPath);
        }

        public async Task<bool> AddProductAsync(Product product)
        {
            try
            {
                var tracking = await _databaseContext.Products.AddAsync(product);
                await _databaseContext.SaveChangesAsync();

                var isAdded = tracking.State == EntityState.Added;

                return isAdded;
            }
            catch(Exception e)
            {
                return false;
            }
        }

        public async Task<IEnumerable<Product>> GetProductAsync()
        {
            try
            {
                var products = await _databaseContext.Products.ToListAsync();
                return products;
            }
            catch(Exception e)
            {
                return null;
            }
        }

        public async Task<Product> GetProductByIdAsync(int id)
        {
            try
            {
                //FindAsyncは暗黙的に主キーで検索されるのか
                //見つからない場合はnullを返す
                var product = await _databaseContext.Products.FindAsync(id);
                return product;
            }
            catch(Exception e)
            {
                return null;
            }
        }

        public async Task<IEnumerable<Product>> QueryProductsAsync(Func<Product, bool> predicate)
        {
            try
            {
                var products = _databaseContext.Products.Where(predicate);
                return products.ToList();
            }
            catch(Exception e)
            {
                return null;
            }
        }

        public async Task<bool> RemoveProductAsync(int id)
        {
            try
            {
                var product = await _databaseContext.Products.FindAsync(id);
                var tracking = _databaseContext.Remove(product);
                await _databaseContext.SaveChangesAsync();

                var isDeleted = tracking.State == EntityState.Deleted;

                return isDeleted;
            }
            catch(Exception e)
            {
                return false;
            }
        }

        public async Task<bool> UpdateProductAsync(Product product)
        {
            try
            {
                var tracking = _databaseContext.Update(product);
                await _databaseContext.SaveChangesAsync();

                var isModified = tracking.State == EntityState.Modified;

                return isModified;
            }
            catch(Exception e)
            {
                return false;
            }
        }
    }

各プラットフォームごとにフォルダのパスを取得する

各プラットフォームのプロジェクト内でそれぞれパスを取得する.

ファイルの保存に適したパスはプラットフォームごとに異なるため. プラットフォームごとのファイル保存に適したフォルダのパスとその取得についてはこちら.

shuhelohelo.hatenablog.com

Android : MainActivity.cs

            var dbPath = Path.Combine(System.Environment.GetFolderPath
                (System.Environment.SpecialFolder.Personal), "productsDB.db");
            var productsRepository = new ProductsRepository(dbPath);

            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            LoadApplication(new App(productsRepository));

iOS : AppDelegate.cs

            SQLitePCL.Batteries_V2.Init();//iOSはこれが必要らしい

            var dbPath = Path.Combine(Environment.GetFolderPath
                (Environment.SpecialFolder.MyDocuments), "..", "Library", "products.db");

            var productsRepository = new ProductsRepository(dbPath);

            global::Xamarin.Forms.Forms.Init();
            LoadApplication(new App(productsRepository));

iOS,AndroidそれぞれのプロジェクトにSqlite.Standardプロジェクトへの参照を追加する.

もちろんAppクラスに引数を受け取るコンストラクタを作る必要がある.

Shared : App.xaml.cs

        public App(IProductsRepository productsRepository)
        {
            InitializeComponent();

            MainPage = new MainPage();
        }

DbContextを作る

これはクラスライブラリ内に作る

    public class DataBaseContext:DbContext
    {
        public DbSet<Product> Products { get; set; }

        private readonly string _databasePath;

        public DataBaseContext(string databasePath)
        {
            _databasePath = databasePath;

            //DBが存在しない場合は作成する.
            //すでにある場合は何もしない.
            //Migrationが行われるということか.
            //Database.EnsureDeleted();//これでDBが削除される
            Database.EnsureCreated();
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite($"Data Source={_databasePath}");
        }
    }

まとめ

まとまらなかったなぁ.

基本的には以下の記事のコマンドラインアプリでSQLiteを使うのと変わらない.

shuhelohelo.hatenablog.com

  • Nugetでインストールして
  • モデルを作って
  • DbContext作って
  • EnsureCreateメソッドを実行

すれば,dbファイルが作成されて使える状態になる.

Xamarin.Forms特有の手順というと,DBファイルのパスの取得あたりだろうか.

写経ソースコード

github.com