🐥note.

小鳥とMicrosoft <3 なエンジニアの技術Blog📚

EntityFrameworkCore.SQLiteのInMemoryDatabase + Repository Patternを書いた備忘録

ローカル環境にDBが必要なアプリの場合、大抵はLiteDbを使ってきました。
今回は趣向を変えてEntityFrameworkCoreを使ってみようかなと思い、Repository Patternな感じで実装してみました。 AdapterはSQLiteを使います。

本エントリはその備忘録です。
作成したコードは以下にUpしてあります。
※ちなみにEntityFrameworkCore初挑戦です。

github.com

IRepository< T >

こんな感じのInterfaceを公開します。

public interface IRepository<T> where T : class, IEntity {
    IQueryable<T> Get ();
    IQueryable<T> Get (Expression<Func<T, bool>> filter = null,
        Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
        string includeProperties = "");
    T GetById (Guid id);
    void Insert (T entity);
    void Update (T entity);
    void Delete (T entity);
    void SaveChange ();
}

filter, orderby, includeProperties付きのI/FはこちらのBlogを参考にさせて頂きました。

GenericRepository< T >

IRepository< T >の実装です。
Stateを書き換える処理は過剰かもしれませんね。
あと実際に使う場合はAsyncとかしないとダメだし_dbSetは危険そうな臭いがしますね。

public class GenericRepository<T> : IRepository<T> where T : class, IEntity {
    private readonly TodoDbContext _context;
    DbSet<T> _dbSet;

    public GenericRepository (TodoDbContext context) {
        this._context = context;
        this._dbSet = _context.Set<T> ();
    }

    public void Delete (T entity) {
        if (_context.Entry (entity).State == EntityState.Detached) {
            _dbSet.Attach (entity);
        }
        _dbSet.Remove (entity);
        SaveChange ();
    }

    public IQueryable<T> Get () {
        return _dbSet;
    }

    public IQueryable<T> Get (Expression<Func<T, bool>> filter = null,
        Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
        string includeProperties = "") {
        IQueryable<T> query = _dbSet;
        if (filter != null) {
            query = query.Where (filter);
        }
        foreach (var includeProperty in includeProperties.Split (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) {
            query = query.Include (includeProperty);
        }
        if (orderBy != null) {
            return orderBy (query);
        } else {
            return query;
        }
    }

    public T GetById (Guid id) {
        return _dbSet.Find (id);
    }

    public void Insert (T entity) {
        _dbSet.Add (entity);
        SaveChange ();
    }

    public void SaveChange () {
        _context.SaveChanges ();
    }

    public void Update (T entity) {
        _dbSet.Attach (entity);
        var entry = _context.Entry (entity);
        entry.State = EntityState.Modified;
        SaveChange ();
    }
}

SQLiteでInMemoryDatabase

全然知らなかったんですが、SQLiteにもInMemoryDatabaseあるんですね。
今回はSQLiteのInMemoryDatabseを使用してみます。

www.sqlite.org

SQLiteのInMemoryDatabaseを使用する場合、connection.open()しておく必要があるようです。

www.meziantou.net

まぁ今回テストで動かすだけなので、DbContextのコンストラクタ内でOpenしちゃいます。

public class TodoDbContext : DbContext
{
    public DbSet<Todo> TodoEntitys { get; set; }
    public DbSet<TodoTask> TodoDetailEntitys { get; set; }

    public TodoDbContext(DbContextOptions<TodoDbContext> options) : base(options)
    {
        // DBのAdapterによっては以下の処理は不要。詳細はTest1Async.csを参照
        this.Database.OpenConnection();
        this.Database.EnsureCreated();
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }
}

実際に使う場合はこんな感じです。

var options = new DbContextOptionsBuilder<TodoDbContext>().UseSqlite("DataSource=testDB;mode=memory;cache=shared").Options;
var context = new TodoDbContext(options);
var repo = new TodoRepository(context);

var entities = repo.Get();

おわり

  • どんな時にどんな内容のSQLを発行しているか分からないのが怖いですね。
    ちゃんと使うなら、そこら辺の理解が必要な感じがします。

  • 個人プロジェクトにEntityFrameworkCoreはtoo muchって予感がする。
    テーブル間でいっぱいリレーション張る予定もないので、LiteDbで十分すぎるんだよなー。

  • とはいえせっかく自由に挑戦できる個人プロジェクトですし、EntityFramework使ってみようかな...。うーん。