Padrão de repositório com o Entity Framework 4.1 e relacionamentos pai / filho
Ainda tenho alguma confusão com o Padrão de Repositório. A principal razão pela qual desejo usar esse padrão é evitar chamar operações de acesso a dados específicas do EF 4.1 do domínio. Prefiro chamar operações CRUD genéricas a partir de uma interface IRepository. Isso tornará os testes mais fáceis e, se precisar alterar a estrutura de acesso a dados no futuro, poderei fazê-lo sem refatorar muito códig
Aqui está um exemplo da minha situação:
Tenho 3 tabelas no banco de dados:Group
, Person
eGroupPersonMap
. GroupPersonMap
é uma tabela de links e consiste apenas noGroup
ePerson
chaves primárias. Criei um modelo EF das 3 tabelas com o designer do VS 2010. A EF foi esperta o suficiente para assumirGroupPersonMap
é uma tabela de links para que não seja exibida no designer. Quero usar meus objetos de domínio existentes em vez das classes geradas pela EF, por isso desativo a geração de código para o model
Minhas classes existentes que correspondem ao modelo EF são as seguintes:
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; }
}
Eu tenho uma interface de repositório genérica assim:
public interface IRepository<T> where T: class
{
IQueryable<T> GetAll();
T Add(T entity);
T Update(T entity);
void Delete(T entity);
void Save()
}
e um repositório EF genérico:
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
}
}
gora, suponha que eu apenas queira mapear umGroup
para um @ existenPerson
. Eu teria que fazer algo como o seguinte:
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);
Mas isso não funciona porque a EF pensa quePerson
é novo e tentará reINSERT
aPerson
no banco de dados que causará um erro de restrição de chave primária. Eu devo usarDbSet
'sAttach
para informar à EF que oPerson
já existe no banco de dados, então apenas crie um mapa entreGroup
ePerson
noGroupPersonMap
mesa
Então, para anexarPerson
para o contexto, agora devo adicionar umAttach
método para meu IRepository:
public interface IRepository<T> where T: class
{
// existing methods
T Attach(T entity);
}
Para corrigir o erro de restrição de chave primária:
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);
Fixo. Agora eu tenho que lidar com outra questão em queGroup
está sendo atualizado no banco de dados toda vez que crio um mapa de grupo / pessoa. Isso ocorre porque no meuEFRepository.Update()
, o estado da entidade é explicitamente definido comoModified'. I must set the Group's state to
Inalteradoso the
tabela do grupo não é modificad
Para corrigir isso, devo adicionar algum tipo deUpdate
sobrecarga no meu IRepository que não atualiza a entidade raiz ouGroup
, nesse caso
public interface IRepository<T> where T: class
{
// existing methods
T Update(T entity, bool updateRootEntity);
}
A implementação EF4 do método Update seria algo como isto:
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();
}
Minha pergunta é: estou abordando isso da maneira certa? Meu Repositório está começando a parecer centrado no EF à medida que começo a trabalhar com o EF e o padrão do repositório. Obrigado por ler este longo post