C#のConsoleでEntityFramework「fluent APIで主キーを定義」

C# コンピュータ
C#

覚えるのが面倒なのでfluent APIを避けてきましたが、エンティティクラスがPOCOなクラスだと何かと都合が良いことに気が付きましたので、fluent APIを試してみたいと思います。

サンプルプログラム

using Microsoft.EntityFrameworkCore;

namespace EF01;

public class LastChange
{
    public int Id { get; set; }
    public string Date { get; set; } = "";
    public string Memo { get; set; } = "";
}

public class SqliteDbContext : DbContext
{
    public DbSet<LastChange> LastChange => Set<LastChange>();
    public string DbPath { get; set; } = "";

    public SqliteDbContext()
    {
        DbPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "/sample.db";
        Console.WriteLine($"Path:{DbPath}");
    }

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

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<LastChange>(entity =>
        {
            // エンティティクラス(LastChange)とテーブルLastChangeを紐づける
            entity.ToTable("LastChange");

            // 主キーの定義... (複合の場合 e => new {e.A, e.B} )
            entity.HasKey(e => e.Id); 

            // プロパティIdに自動採番属性を付与
            entity.Property(e => e.Id)
                  .ValueGeneratedOnAdd();

            // プロパティDateにNOT NULL属性を付与
            entity.Property(e => e.Date)
                  .IsRequired();

            // プロパティMemoにNOT NULL属性を付与
            entity.Property(e => e.Memo)
                  .IsRequired();
        });
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

        var db = new SqliteDbContext();

        // テーブル作成
        db.Database.EnsureCreated();

        // レコード件数が0の場合
        if (!db.LastChange.Any())
        {
            db.Add(new LastChange { Date = DateTime.Now.ToString("yyyy/MM/dd"), Memo = "OilChange" });
            db.Add(new LastChange { Date = DateTime.Now.ToString("yyyy/MM/dd"), Memo = "Haircut" });
            db.SaveChanges();
        }

        // 表示
        foreach (var r in db.LastChange)
        {
            Console.WriteLine("{0} {1} {2}", r.Id, r.Date, r.Memo);
        }

        // 更新
        var f = db.LastChange.Single(x => x.Memo == "Haircut");
        if (f != null)
        {
            f.Memo = "Sanpatu";
            db.SaveChanges();
        }

        // 表示
        foreach (var r in db.LastChange)
        {
            Console.WriteLine("{0} {1} {2}", r.Id, r.Date, r.Memo);
        }

        // 削除
        foreach (var r in db.LastChange.ToList())
        {
            db.LastChange.Remove(r);
            db.SaveChanges();
        }
    }
}

説明

変更点としてエンティティクラスLastChangeで主キーなどの属性が無くなり、代わりにOnModelCreating()に主キーなどの定義が行われるようになっています。

こちらのサンプルコードだと、LastChangeがPOCOなクラスになったとしても特別なメリットは無さそうですが、メンバーがプロパティですのでインターフェイスにすることが出来て、他のクラスに利用したり、単体テストをしたりと再利用がしやすくなると考えられます。

ただ、本格的に再利用を考えて疎結合にする場合、DbContext自体もインタフェースなどで抽象化しないとメリットが出てこないと思われます。多分定番ノードがあると思うので探してみたいと思います。

コメント