読者です 読者をやめる 読者になる 読者になる

しばやん雑記

ASP.NET とメイドさんが大好きなフリーランスのプログラマーのブログ

Repository パターンふたたび

今までの Repository パターンについて書いたエントリ。

Repository for ASP.NET MVC - しばやん雑記
Repository まとめ - しばやん雑記

もうこれを書いたのも 2 年前なんですねー。その間に LINQ to SQL はいらない子になっていたり、Entity Framework 4 が出たと思ったらコードファースト対応の CTP が出て「ひょっほーい!ぶーん!」やってたり、いろいろと当時とは変わってます。

前回は DataContext の共有とかどうしよう?で話を終わらせてたんですけど、冷静に考えると整合性はエンティティに定義したナビゲーションプロパティに対して操作を行うから心配する必要はないと思いました。じゃあ DbContext に Repository をフィールドとして持たせればいいよね。

てことで、Entity Framework CTP を使うようにしたりいろいろと修正。

IRepository インターフェース

public interface IRepository<TEntity> where TEntity : class
{
    TEntity Find(params object[] keyValues);

    IList<TEntity> ListAll();
    IList<TEntity> FindAll(Expression<Func<TEntity, bool>> predicate);

    void Add(TEntity entity);
    void Remove(TEntity entity);

    void Save();
}

Repository 抽象クラス

public abstract class Repository<TContext, TEntity> : IRepository<TEntity>
    where TContext : DbContext, new()
    where TEntity : class
{
    protected Repository()
    {
        _context = new TContext();
        _set = _context.Set<TEntity>();
    }

    private readonly DbContext _context;
    private readonly IDbSet<TEntity> _set;

    public TEntity Find(params object[] keyValues)
    {
        return _set.Find(keyValues);
    }

    public IList<TEntity> ListAll()
    {
        return _set.ToList();
    }

    public IList<TEntity> FindAll(Expression<Func<TEntity, bool>> predicate)
    {
        return _set.Where(predicate).ToList();
    }

    public void Add(TEntity entity)
    {
        _set.Add(entity);
    }

    public void Remove(TEntity entity)
    {
        _set.Remove(entity);
    }

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

Repository に IDisposable を実装した方が良いのかなーとか、ちょっと考える所はありますがこんなもんでしょう。
使い方は簡単、Product と ProductContext を作成済みなら、以下のようにクラスを作ります。

public class ProductRepository : Repository<ProductContext, Product>
{
}

後は ProductRepository をインスタンス化すれば使えます。前回書いたものよりシンプルになりましたね。