目標
WPF のメインプロジェクトと DB のプロジェクトを分離すること。
DB のプロジェクトを分離しておくのは、無難だと思います。メインプログラムに DB を含めてしまうと、後から DB を参照できないプロジェクトなどが出てきたりするのではないかと思います。
C# 開発中のライブラリー参照は、双方向にできない (しないようにする) ものなのもあって、私的には推奨です。
どちらのプロジェクトにも NuGet で System.Data.SQLite をインストールしています。
SQLite のインストール
NuGet からインストールします。このときのバージョンは 1.0.107 でした。
ライブラリーとメインプログラムのそれぞれにインストールしました。
App.config
SQLite をインストール時に自動的に更新される App.config です。
個人的に App.config は使い勝手があまり良いと思わないので、コーディング DbConfiguration で別途指定しました。そのため、 App.config の内容は簡単です。 ( DB と直接の関係が薄い)
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> </startup> </configuration>
DbConfiguration の記述
App.config で設定している内容 (その一部) をコーディングで設定します。
コード化すると、デバッグ時の情報が拾いやすいです。コンソールにデバッグ用の情報を出力しておくと安心です。
一度うまく設定できれば後から問題になることは少ないので、好みもあると思います。今回のようにシンプルな接続をするだけのものでは、メリットはないかもしれません。
using System; using System.Data.Entity; using System.Data.Entity.Core.Common; using System.Data.SQLite; using System.Data.SQLite.EF6; public class SQLiteConfiguration : DbConfiguration { public SQLiteConfiguration() { SetProviderFactory("System.Data.SQLite", SQLiteFactory.Instance); SetProviderFactory("System.Data.SQLite.EF6", SQLiteProviderFactory.Instance); SetProviderServices("System.Data.SQLite", (DbProviderServices)SQLiteProviderFactory.Instance.GetService(typeof(DbProviderServices))); Console.WriteLine("SQLite Providers の初期化を実行しました。"); } }
O/R マッピング
O/R マッピングのデータ類を準備する方法は、いろいろあります。
先にコードを書いて、クラスから自動的に DB のテーブルを設計する (自動マイグレーション) といったコードファーストのやり方は有名です。
個人的な感想ですが (今のところ) SQLite の DB ではこうした機能は期待しないほうがよいと考えています。
例えば、ナビゲージション プロパティがうまく機能しなかったり、 DateTimeOffset の変換に工夫が必要だったり、 +α の機能はサポートにこまると思います。意識せずに利用をすると、面倒と付き合うことになると思います。事前にテストコードを書いてみてください。
今回も手間を買っています。先に DB ファイルを用意して、それに合ったクラスをそれぞれ準備しました。
たしかに、少し手間ですが、よほど巨大な設計をしない限り、どのくらいの時間で終わる単純作業か見積もれるので SQLite のときはこうしてしまいます。
[Table("users")] public class User { [Key] [Column("id")] public int Id { get; set; } [Column("name")] public string Name { get; set; } [Column("data1")] public int Data1 { get; set; } [Column("data2")] public string Data2 { get; set; } public virtual ICollection<UserParameter> Params { get; set; } }
DbContext
DB のレコードを取り出すときに利用する Context クラスです。
面倒なら、この部分で DbConnection を具体的に SQLiteConnection にしてもよい。 (途中で、複数の DB に対応する、変更するなら抽象化しておいたほうがよい)
public class MasterContext : DbContext { public DbSet<User> Users { get; set; } public MasterContext(DbConnection connection) : base(connection, true) { } static MasterContext() { // エラーメッセージを消す //SQLite error (1): no such table: __MigrationHistory //SQLite error (1): no such table: __MigrationHistory //SQLite error (1): no such table: EdmMetadata System.Data.Entity.Database.SetInitializer<MasterContext>(null); } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); base.OnModelCreating(modelBuilder); } }
動作チェック
単純に User クラスの DB レコードを全部 Select して、書き出ししています。
本番時なら connection のテキストは、 SQLiteConnectionStringBuilder を使ってテキストを作成し Context に接続したほうがきれいです。
private void Button_Click(object sender, RoutedEventArgs e) { var connection = @"version=3;data source=Sample.db;cache size=10000;default timeout=5000;busytimeout=10000;foreign keys=True"; using (var context = new MasterContext(new SQLiteConnection(connection))) { var users = context.Users.ToList(); foreach (var user in users) { Label.Content += $"{user.Name} の技は {user.Data2} です。\r\n"; } } }
サンプルコード
参考
Windows Presentation Foundation プログラミング入門
- 作者:赤坂 玲音
- 発売日: 2007/04/26
- メディア: 単行本