私の経験では、EF は DbSet を通じてこのパターンを既に実装しているため、独自のリポジトリを作成することは冗長です。
最近のプロジェクトで MVC3 + EF Code Fisrt を使用しました。いくつかのチュートリアルに従って汎用リポジトリの実装を開始しましたが、すぐに不要で冗長なコードをたくさん書いていることに気付きました。実際、リポジトリには、DbSet の多くの機能が隠されているだけでした。最後に、それらを削除して、DbContext と DbSet を直接操作することにしました。
しかし、単純な CRUD 操作を超えた複雑なビジネス ロジックはどうでしょうか。
クエリや複数の CRUD 操作などのすべての複雑な機能を、サービス レイヤーを介して公開しました。機能ごとに異なるサービス クラスを構築できます。たとえば、AccountService を記述して、ユーザー アカウントに関連するすべての機能を管理できます。このようなもの:
public class AccountService {
private MyContext ctx;
public AccountService(DbContext dbContext) {
this.ctx = (MyContext)dbContext;
}
/// <summary>
/// Gets the underlying DbContext object.
/// </summary>
public DbContext DbContext {
get { return ctx; }
}
/// <summary>
/// Gets the users repository.
/// </summary>
public DbSet<User> Users {
get {return ctx.Users;}
}
public bool ValidateLogin(string username, string password) {
return ctx.Users.Any(u => u.Username == username && u.Password == password);
}
public string[] GetRolesForUser(string username) {
var qry = from u in ctx.Users
from r in u.Roles
where u.Username == username
select r.Code;
return qry.ToArray<String>();
}
public User CreateUser(string username, string password) {
if (String.IsNullOrWhiteSpace(username)) throw new ArgumentException("Invalid user name");
if (String.IsNullOrWhiteSpace(password)) throw new ArgumentException("Invalid password");
User u = new User {
Username = username.Trim().ToLower(),
Password = password.Trim().ToLower(),
Roles = new List<Role>()
};
ctx.Users.Add(u);
ctx.SaveChanges();
return u;
}
依存性注入はどうですか?
このアプローチを使用して、挿入する必要があるのは DbContext だけです。サービス クラスには、DbContext を受け取るコンストラクターがあります。そのため、コントローラー コンストラクターがサービス インスタンスを取得すると、DbContext が注入されます。
編集: コード例
これは、コントローラーがどのように見えるかについてのコード例です。
public class HomeController : Controller {
private readonly AccountService accountService;
public AccountController(AccountService accountService) {
this.accountService = accountService;
}
}
そして、これは NInject を使用した DI 構成である可能性があります。
private static void RegisterServices(IKernel kernel) {
kernel.Bind<MyContext>().ToSelf().InRequestScope();
kernel.Bind<DbContext>().ToMethod(ctx => ctx.Kernel.Get<MyContext>());
}
単体テストはどうですか?
サービス層クラスごとに特定のインターフェイスを構築し、必要な場所でそれをモックできます。