頭ん中

しがないITエンジニアが、考えた事を書きます。

AccessをEntityFrameworkでCodeFirstに扱ってみる。

EntityFrameworkはOleDBに非対応で事実上利用不可、UWPではついにSQLiteにその座を奪われたりと、もはや過去の遺物みたいな扱いを受けてるAccessですが、Windowsのちょろっとした業務アプリなんかには、未だに多く使われていることと思います。
何年か前に「AccessでEntityFrameworkは無理」という話をどこかしこで見たので、てっきり無理なものだと思い込んでいたんですが、偶々NuGetでJetEntityFrameworkなるものを見つけたので、試してみました。

今更EFなんて~と思う人も多いと思うんですが、Accessがネックになって、EFが今風に見えてる&だけど手の届かないもの、という人/現場/アプリも結構あると思うんですよね。 このJetEntityFrameworkが2015年にできて、未だにそれなりに更新されているあたり、Accessの根強い人気(?)根深い闇(?)を感じます。

Microsoft Access Entity Framework Provider - CodePlex

環境等

  • Visual Studio 2015 Update3
  • Access 2010(.accdb 2007-2010形式)
  • JetEntityFrameworkProvider v1.2.6

作ってみる

新規プロジェクトの作成

  • Windowsクラシックなアプリ(.Net 4.6.1)を新規作成します。 VS New Project 今回はC#WPFを選んでいますが、Accessを用いたデスクトップアプリといえば、やっぱりVBWindowsフォーム!という場合もご安心を。同じ手順で実装可能でした。

新規データベースの作成

  • Accessからデータベースファイルを作成します。ここではC:\Temp\Main.accdbに置きました。 Access New Database この時点ではまだテーブルも何も存在しません。
    ちなみにこのJetEntityFrameworkですがaccdbでなくmdbも対応しているそうです(OleDbでいけるから当然といえば当然)。

ライブラリインストール

NuGetパッケージマネージャを開き、JetEntityFrameworkをインストールします。 (依存関係のEntityFramework6は勝手についてきます。) SnapCrab_NoName_2017-3-4_23-10-8_No-00.png

データベースの接続定義

App.configに先ほど作成したAccessデータベースのConnectionStringsを追記します。 MainConnectionの名前で作成しました。

  <connectionStrings>
    <add name="MainConnection" connectionString="Provider=Microsoft.Ace.OleDb.12.0;Data Source = C:\Temp\Main.accdb"
         providerName="JetEntityFrameworkProvider"/>
  </connectionStrings>

POCO Entityクラスの作成

コードファーストなので、まずはモデルを表すコードを書きます。 クラス名がテーブル名、プロパティがフィールド名になります。

  • 仮に商品マスターを分類管理するようなイメージでShohin``Bunrui2つのEntityクラスを作ってみます。
    class Shohin
    {
        public int Sku { get; set; }
        public string Jan { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public virtual List<Bunrui> Bunrui { get; set; }
    }

    class Bunrui
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public virtual List<Shohin> Shohin { get; set; }
    }

Contextクラスの作成

Entityクラスとデータベースの橋渡しとなるのがコンテキストクラスです。 DbContextを継承したContextクラスを作成します。

  • System.Data.Entity名前空間のクラスを使っていきます。
using System.Data.Entity;
  • 継承元のコンストラクタに先ほどApp.configで定義したMainConnectionを引数で渡すようにしておきます。 これで、このContextクラスを使用する際に、接続情報として使用されます。
  • Fluent APIを利用し、OnModelCreatingメソッド内でShohinクラスの主キーにSkuプロパティを指定しています。
    (EFの規約ではプロパティ名がIdのものが主キーとされるため、Idが無いShohinクラスはこのままでは使えません。)
    class Context:DbContext
    {
        public Context() : base("MainConnection")
        {
        }

        public DbSet<Shohin> Shohin { get; set; }
        public DbSet<Bunrui> Bunrui { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Shohin>()
                .HasKey( _ => _.Sku);
        }
    }

アプリ起動、テーブル生成

準備は整ったので、実際にテーブルが生成されるところを確認します。

  • メインフォームのコンストラクタに、先ほどのContextクラスを使う処理を追記。とりあえず商品マスタの個数を数える感じで。
        public MainWindow()
        {
            InitializeComponent();

            using (var context = new Context())
            {
                Console.WriteLine(context.Shohin.Count());      
            }
        }
  • アプリをビルド&実行します。 ここまでにミスが無ければ、さくっとアプリが立ち上がると思います。

  • Accessデータベースを見てみます。 ShohinBunruiに多対多の関係を持たせたので、テーブルShohinsBubruis、そして中間テーブルShohinBunruisの3テーブルが作成されました。賢い! 無題.png (ローマ字使ってしまったので勝手に複数形にされて残念なテーブル名になってます)