はじめに
先日、Xamarin.iOS で EntityFrameworkCore が使えることがわかった。
tnakamura.hatenablog.com
そうなると、データベースのマイグレーションがやりたくなるのは自然な流れ。
今度はマイグレーションを試してみた。
マイグレーションファイルを手書きするのは人がやることじゃないので、
ツールで生成したいところ。
Microsoft.EntityFrameworkCore.Tools をパッケージ参照すれば、
生成コマンド Add-Migration
が使えるようになる。
www.nuget.org
ただ、Xamarion.iOS プラットフォームをサポートしていなかったので、Add-Migration
を実行するとエラー発生。
Xamarin.iOS プロジェクトにパッケージを追加できたから、期待してしまったじゃないか。
仕方ないのでマイグレーションファイル生成用に .NET Core プロジェクトを作成
.NET Core コンソールアプリケーションをプロジェクトに追加し、
DbContext はそちらに用意する。
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using System;
namespace HelloXamarin.Core
{
public class Stamp
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public DateTime StampedAt { get; set; } = DateTime.UtcNow;
}
public class ApplicationDbContext : DbContext
{
readonly string databasePath;
public ApplicationDbContext(string databasePath)
: base()
{
this.databasePath = databasePath;
Stamps = Set<Stamp>();
}
public DbSet<Stamp> Stamps { get; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite(
connectionString: $"Filename={databasePath}");
base.OnConfiguring(optionsBuilder);
}
}
public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext CreateDbContext(string[] args)
{
return new ApplicationDbContext("test.db");
}
}
}
.NET Core プロジェクトをスタートアッププロジェクトにして
Add-Migration CreateInitialSchema
を実行。
すると、下記のような内容の 20180406052413_CreateInitialSchema.cs が出力された。
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace HelloXamarin.Core.Migrations
{
public partial class CreateInitialSchema : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Stamps",
columns: table => new
{
Id = table.Column<string>(nullable: false),
StampedAt = table.Column<DateTime>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Stamps", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Stamps");
}
}
}
他にも、20180406052413_CreateInitialSchema.Designer.cs と ApplicationDbContextModelSnapshot.cs も生成されたが、
誌面の都合で省略。
アプリ起動時にマイグレーションを実行するように修正
ApplicationDbContext とマイグレーション用のソースコード一式を、
Xamarin.iOS プロジェクトにリンクとして追加する。
using Foundation;
using HelloXamarin.Core;
using Microsoft.EntityFrameworkCore;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UIKit;
namespace HelloXamarin
{
public class Application
{
static void Main(string[] args)
{
UIApplication.Main(args, null, "AppDelegate");
}
}
[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
public override UIWindow Window { get; set; }
public ApplicationDbContext DbContext { get; private set; }
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
SQLitePCL.Batteries_V2.Init();
var dbPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"..",
"Library",
"helloxamarin.db");
DbContext = new ApplicationDbContext(dbPath);
DbContext.Database.Migrate();
Window = new UIWindow(UIScreen.MainScreen.Bounds);
Window.RootViewController = new UINavigationController(
new MainViewController(DbContext));
Window.MakeKeyAndVisible();
return true;
}
public override void WillTerminate(UIApplication application)
{
DbContext?.Dispose();
}
}
public class MainViewController : UITableViewController
{
readonly ApplicationDbContext _dbContext;
Stamp[] _stamps = new Stamp[0];
UIBarButtonItem _addButton;
public MainViewController(ApplicationDbContext dbContext)
: base(UITableViewStyle.Plain)
{
Title = "Hello Xamarin";
_dbContext = dbContext;
_addButton = new UIBarButtonItem(
UIBarButtonSystemItem.Add,
HandleAddButtonClick);
}
async void HandleAddButtonClick(object sender, EventArgs e)
{
await AddStampAsync();
await LoadStampsAsync();
TableView.InsertRows(
new[] { NSIndexPath.FromItemSection(0, 0) },
UITableViewRowAnimation.Fade);
}
public override async void ViewDidLoad()
{
base.ViewDidLoad();
NavigationItem.RightBarButtonItem = _addButton;
await LoadStampsAsync();
}
public override nint RowsInSection(UITableView tableView, nint section)
{
return _stamps.Length;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var cell = new UITableViewCell(UITableViewCellStyle.Default, "StampCell");
var stamp = _stamps[indexPath.Row];
cell.TextLabel.Text = stamp.StampedAt.ToString();
return cell;
}
async Task AddStampAsync()
{
_dbContext.Stamps.Add(new Stamp());
await _dbContext.SaveChangesAsync();
}
async Task LoadStampsAsync()
{
_stamps = await _dbContext.Stamps
.OrderByDescending(s => s.StampedAt)
.ToArrayAsync();
}
}
}
アプリを起動するとマイグレーションが実行された!
おわりに
EntityFrameworkCore のマイグレーションが使えるのは良かったが、
現状だとそのためにわざわざ .NET Core などの別プロジェクトを用意しないといけないので面倒。
正直使うかどうか悩ましいな。