私が書いたコードについて気にしないで、既存の抽象化を抽象化することについての議論が必要な場合は、最後の3つの段落に進んでください。私は、汎用リポジトリを使用してEntity Frameworkの上にUnit Of Workパターンを実装しました。私は実際に何を達成しましたか?
私が見ていたPluralsightレッスンを介して、Entity Frameworkの上にRepositoriesとUnit Of Workというアイディアを導入しました。私はこのプロセスを詳述したマイクロソフト自身のページにもスタブをつけた:http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
だから私はそれを試してみることにした。 Entity Frameworkの上にジェネリックリポジトリを持つ自分のUnit Of Workクラスを作成しました。私は自分のモックをテスト用に注入できるように、インターフェイスを利用してすべてのことを絶対に行いました。
まず最初に、この練習問題では簡単なブログアプリケーションを作成しました。
私はDbContextを使い始めました。私はインターフェイスを使用することを確認してください!
public interface ISimpleBlogContext : IDisposable
{
IDbSet<Blog> Blogs { get; }
IDbSet<Post> Posts { get; }
void SaveChanges();
IDbSet<T> Set<T>() where T : class;
DbEntityEntry Entry<T>(T entity) where T : class;
}
は、私は確信して誰もがIDbSetsをするためのものであるかを知っているんだけど、SaveChangesメソッドは、設定、およびエントリの方法は場違いに少し見えるかもしれません。心配しないで、後でそれらに行きます。
は、だから今、私は、実際の具体的なDbContextに私のインターフェイスをフックアップ:
public class SimpleBlogContext : DbContext, ISimpleBlogContext
{
public SimpleBlogContext() {
Database.SetInitializer<SimpleBlogContext>(new DropCreateDatabaseAlways<SimpleBlogContext>());
}
public IDbSet<Blog> Blogs { get; set; }
public IDbSet<Post> Posts { get; set; }
public DbEntityEntry Entry<T>(T entity) where T : class
{
return Entry<T>(entity);
}
void ISimpleBlogContext.SaveChanges()
{
SaveChanges();
}
IDbSet<T> ISimpleBlogContext.Set<T>()
{
return Set<T>();
}
データベースの初期化子は、ちょうどこのテストアプリケーションのためのデータベースが削除されることを保証し、私はアプリを実行するたびに再作成されます。結局のところ、これは単なる運動であり、実際のアプリではありませんでした。ここで実装されているSaveChanges、Set、およびEntryメソッドを見ることができます。これらは、すべて同じ名前のDbContextメソッドのラッパーです。
だから今上のリポジトリへ...
この場合、私は唯一の風よが、私は(私は自分のアプリケーションに追加される場合がありますすべてのエンティティのための再書き込みを事実上同じリポジトリのコードにつもりはなかったです私は一般的なリポジトリを作った。インターフェイスをスキップしないでください!
public interface IGenericRepository<T>
where T : class
{
IEnumerable<T> GetAll();
T GetById(object id);
IEnumerable<T> GetByExpression(Expression<Func<T, bool>> expression);
void Add(T entity);
void Delete(T entity);
void Update(T entity);
}
と具体的なバージョン...(私はすべてのものをテストすることができるようにしたいので、私は具体的なDbContextクラスの代わりに、ここに私のISimpleBlogContextを使用しています注意してください。また、今あなたは私が持っていた理由を知っていますこれらの設定、エントリを書き、そして私のISimpleBlogContextインターフェイスでのSaveChangesメソッド)
public class GenericRepository<T> : IGenericRepository<T>
where T : class
{
public GenericRepository(ISimpleBlogContext context)
{
this.context = context;
}
private ISimpleBlogContext context;
public void Add(T entity)
{
context.Set<T>().Add(entity);
}
public void Delete(T entity)
{
context.Set<T>().Remove(entity);
}
public IEnumerable<T> GetAll()
{
return context.Set<T>().ToList<T>();
}
public IEnumerable<T> GetByExpression(Expression<Func<T, bool>> expression)
{
return context.Set<T>().Where<T>(expression).ToList<T>();
}
public T GetById(object id)
{
return context.Set<T>().Find(id);
}
public void Update(T entity)
{
context.Entry(entity).State = EntityState.Modified;
}
}
と作業クラスの今ようやく、ユニット
public class UnitOfWork : IDisposable
{
public void Dispose()
{
if (context != null)
{
context.Dispose();
context = null;
}
}
public UnitOfWork()
{
context = new SimpleBlogContext();
}
public UnitOfWork(ISimpleBlogContext context)
{
this.context = context;
}
private ISimpleBlogContext context;
public GenericRepository<TEntity> GetRepository<TEntity>() where TEntity : class
{
return new GenericRepository<TEntity>(context);
}
public void Save()
{
context.SaveChanges();
}
}
私はまだISimpleBlogContextを許可していますオーバーロードされたコンストラクタを介して渡されますが、デフォルトのコンストラクタは、最終的に私たちの具体的なSimpleBlogContext DbContextを取得します。
これですべてをテストするだけです。だから、私は単純なコンソールアプリケーションを書いています。これは、偽のブログをいくつか生成し、Unit Of Workクラスを使ってそれらを保存するだけのものです。その後、ブログと投稿をループして印刷して、実際にデータベースに保存されていることを確認できます。
P.S.ジェイクはあなたが吠えることについて疑問に思っている場合のために私の犬です...
class Program
{
static void Main(string[] args)
{
UnitOfWork unitOfWork = new UnitOfWork();
GenericRepository<Blog> blogRepository = unitOfWork.GetRepository<Blog>();
Blog paulsBlog = new Blog()
{
Author = "Paul",
Posts = new List<Post>()
{
new Post()
{
Title = "My First Post",
Body = "This is my first post"
},
new Post()
{
Title = "My Second Post",
Body = "This is my second post"
}
}
};
Blog jakesBlog = new Blog()
{
Author = "Jake",
Posts = new List<Post>()
{
new Post()
{
Title = "Dog thoughts",
Body = "Bark bark bark"
},
new Post()
{
Title = "I like barking",
Body = "Bark bark bark"
}
}
};
blogRepository.Add(paulsBlog);
blogRepository.Add(jakesBlog);
unitOfWork.Save();
List<Blog> blogs = blogRepository.GetAll() as List<Blog>;
foreach (Blog blog in blogs)
{
System.Console.WriteLine("ID: {0}, Author: {1}\n", blog.Id, blog.Author);
if (blog.Posts != null && blog.Posts.Count > 0)
{
foreach (Post post in blog.Posts)
{
System.Console.WriteLine("Posted at: {0}, Title: {1}, Body: {2}", post.PostTime, post.Title, post.Body);
}
}
else
{
System.Console.WriteLine("No posts");
}
System.Console.WriteLine("\n");
}
}
}
これは機能します。わーい!
私の質問は、単純に...私はこれを行うことで正確に何を得ましたか?
DbContextはすでに単体作業ユニットではなく、DbSetはすでにリポジトリですか?私がしたことは、機能を追加していないものの両方のための本当に精巧なラッパーを書くことだったようです。すべてがインターフェイスを使用しているので、よりテストに優しいと言えるかもしれませんが、Moqのような模擬フレームワークでは、DbSetsとDbContextをモックすることはすでに可能です。私のリポジトリは一般的なものなので、ビジネスロジックに固有の機能は文字通りゼロです。 Unit Of WorkクラスはDbContextの単なるラッパーであり、汎用リポジトリはDbSetの単なるラッパーです。
なぜ誰かがこれを行う理由を誰かに説明できますか?私は〜4時間Pluralsightレッスンを見て、実際にそれを自分でやっているすべての手間を経て、それでも私はそれを取得しません。
コードbloat以外何も得られていません – ErikEJ
DbContextは単体作業単位として意味があります。もちろん、それは基本的にDALなので、作業単位ではありません。 DbSetは独自のリポジトリではなく、IQueryableのものであり、これが意味するすべての利点と問題があります。それ以外は、機能をカプセル化するラッパーです。 Moqを使ったことはありませんでしたが、そこにインターセプタを実装することはできないため、DbContextを模倣するのがうまくいくかどうかはわかりません。だから、ほとんどの場合、ジェネリックパターンと実際の実行タスクの再配布を持つラッパーです。 –
DevilSuichiro
これは[古い議論](https://ayende.com/blog/3955/repository-is-the-new-singleton)です。あなたは何も得られず、柔軟性を失います。そして、リポジトリはコンテキストを破棄すべきではありません。 –