リポジトリパターンとはまだ混乱しています。このパターンを使用する主な理由は、ドメインからEF4.1固有のデータアクセス操作を呼び出さないようにするためです。IRepositoryインターフェースからジェネリックCRUD操作を呼び出したいと思います。これにより、テストが容易になり、将来データアクセスフレームワークを変更する必要が生じた場合でも、多くのコードをリファクタリングすることなく変更できるようになります。
これが私の状況の例です:
Group
データベースに、、、Person
およびの3つのテーブルがありますGroupPersonMap
。GroupPersonMap
はリンクテーブルであり、主キーGroup
とPerson
主キーのみで構成されます。VS2010デザイナーで3つのテーブルのEFモデルを作成しました。EFは、リンクテーブルであると想定するのに十分賢いGroupPersonMap
ので、デザイナーには表示されません。EFで生成されたクラスの代わりに既存のドメインオブジェクトを使用したいので、モデルのコード生成をオフにします。
EFモデルに一致する私の既存のクラスは次のとおりです。
public class Group
{
public int GroupId { get; set; }
public string Name { get; set; }
public virtual ICollection<Person> People { get; set; }
}
public class Person
{
public int PersonId {get; set; }
public string FirstName { get; set; }
public virtual ICollection<Group> Groups { get; set; }
}
私は次のような一般的なリポジトリインターフェイスを持っています:
public interface IRepository<T> where T: class
{
IQueryable<T> GetAll();
T Add(T entity);
T Update(T entity);
void Delete(T entity);
void Save()
}
および一般的なEFリポジトリ:
public class EF4Repository<T> : IRepository<T> where T: class
{
public DbContext Context { get; private set; }
private DbSet<T> _dbSet;
public EF4Repository(string connectionString)
{
Context = new DbContext(connectionString);
_dbSet = Context.Set<T>();
}
public EF4Repository(DbContext context)
{
Context = context;
_dbSet = Context.Set<T>();
}
public IQueryable<T> GetAll()
{
// code
}
public T Insert(T entity)
{
// code
}
public T Update(T entity)
{
Context.Entry(entity).State = System.Data.EntityState.Modified;
Context.SaveChanges();
}
public void Delete(T entity)
{
// code
}
public void Save()
{
// code
}
}
Group
ここで、既存のものを既存のものにマップしたいとしますPerson
。私は次のようなことをしなければならないでしょう:
EFRepository<Group> groupRepository = new EFRepository<Group>("name=connString");
EFRepository<Person> personRepository = new EFRepository<Person>("name=connString");
var group = groupRepository.GetAll().Where(g => g.GroupId == 5).First();
var person = personRepository.GetAll().Where(p => p.PersonId == 2).First();
group.People.Add(person);
groupRepository.Update(group);
ただし、EFは新しいと見なし、データベースに再入力しようとするため、これは機能しません。Person
これにより、主キー制約エラーが発生します。のメソッドを使用して、データベースにすでに存在することをEFに通知する必要があるため、テーブルとテーブルの間にマップを作成するだけです。INSERT
Person
DbSet
Attach
Person
Group
Person
GroupPersonMap
したがってPerson
、コンテキストにアタッチするにはAttach
、IRepositoryにメソッドを追加する必要があります。
public interface IRepository<T> where T: class
{
// existing methods
T Attach(T entity);
}
主キー制約エラーを修正するには:
EFRepository<Group> groupRepository = new EFRepository<Group>("name=connString");
EFRepository<Person> personRepository = new EFRepository<Person>(groupRepository.Context);
var group = groupRepository.GetAll().Where(g => g.GroupId == 5).First();
var person = personRepository.GetAll().Where(p => p.PersonId == 2).First();
personRepository.Attach(person);
group.People.Add(person);
groupRepository.Update(group);
修理済み。Group
ここで、グループ/個人マップを作成するたびにデータベースで更新される別の問題に対処する必要があります。これは、私のEFRepository.Update()
メソッドでは、エンティティの状態が明示的にUnchanged Group`に設定されてModified'. I must set the Group's state to
いるため、so the
テーブルが変更されないためです。
これを修正するにはUpdate
、ルートエンティティを更新しない何らかのオーバーロードをIRepositoryに追加する必要がありますGroup
。この場合、次のようになります。
public interface IRepository<T> where T: class
{
// existing methods
T Update(T entity, bool updateRootEntity);
}
UpdateメソッドのEF4の意味は、次のようになります。
T Update(T entity, bool updateRootEntity)
{
if (updateRootEntity)
Context.Entry(entity).State = System.Data.EntityState.Modified;
else
Context.Entry(entity).State = System.Data.EntityState.Unchanged;
Context.SaveChanges();
}
私の質問は:私はこれに正しい方法でアプローチしていますか?EFとリポジトリパターンを使い始めると、リポジトリはEF中心に見え始めています。この長い投稿を読んでくれてありがとう