Todavía tengo cierta confusión con el patrón de repositorio. La razón principal por la que deseo utilizar este patrón es evitar llamar operaciones de acceso a datos específicos de EF 4.1 desde el dominio. Prefiero llamar operaciones CRUD genéricas desde una interfaz IRepository. Esto facilitará las pruebas y si alguna vez tengo que cambiar el marco de acceso a datos en el futuro, podré hacerlo sin refactorizar una gran cantidad de código.Patrón de repositorio con Entity Framework 4.1 y relaciones padre/hijo
Aquí es un ejemplo de mi situación:
Tengo 3 tablas en la base de datos: Group
, Person
y GroupPersonMap
. GroupPersonMap
es una tabla de enlace y solo consiste en las claves primarias Group
y Person
. Creé un modelo EF de las 3 tablas con el diseñador de VS 2010. EF fue lo suficientemente inteligente como para asumir que GroupPersonMap
es una tabla de enlace, por lo que no se muestra en el diseñador. Quiero utilizar mis objetos de dominio existentes en lugar de las clases generadas por EF, así que desactivo la generación de código para el modelo.
Mis clases existente que coincida con el modelo de EF son los siguientes:
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; }
}
que tienen una interfaz genérica repositorio de este modo:
public interface IRepository<T> where T: class
{
IQueryable<T> GetAll();
T Add(T entity);
T Update(T entity);
void Delete(T entity);
void Save()
}
y un repositorio EF genérica:
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
}
}
Ahora supongamos que solo quiero asignar un Group
existente a unexistente. Tendría que hacer algo como lo siguiente:
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);
Pero esto no funcionará porque piensa EF Person
es nuevo, y voy a tratar de volver a la INSERT
Person
en la base de datos que provocará un error de restricción de clave primaria . Debo utilizar Attach
método DbSet
's para decir que el EF Person
ya existe en la base de datos por lo que acaba de crear un mapa entre Group
y Person
en la tabla GroupPersonMap
.
Así que el fin de unir a Person
el contexto ahora debo añadir un método Attach
a mi IRepository:
public interface IRepository<T> where T: class
{
// existing methods
T Attach(T entity);
}
Para solucionar el error de clave principal limitación:
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);
fijo. Ahora tengo que lidiar con otro problema donde Group
se está ACTUALIZANDO en la base de datos cada vez que creo un mapa de Grupo/Persona. Esto es porque en mi método EFRepository.Update()
, el estado de la entidad se establece explícitamente en Modified'. I must set the Group's state to
Sin cambios so the
La tabla del grupo `no se modifica.
Para solucionar esto, debo añadir algún tipo de Update
sobrecarga a mi IRepository que no se actualiza la entidad raíz, o Group
, en este caso:
public interface IRepository<T> where T: class
{
// existing methods
T Update(T entity, bool updateRootEntity);
}
La implementació EF4 del método de actualización podría ser algo de esta manera:
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();
}
Mi pregunta es: ¿estoy acercando este el camino correcto? Mi Repositorio está empezando a verse EF céntrico cuando empiezo a trabajar con EF y el patrón de repositorio. Gracias por leer esta larga publicación
1 Gran respuesta –
1 Muy informativo –
1 de marco de la entidad + unidad de trabajo. Los repositorios necesitan compartir el mismo dbcontext. Apuesto que ese es mi problema. – Jhayes2118