Ho ancora un po 'di confusione con il pattern di repository. Il motivo principale per cui voglio utilizzare questo modello è di evitare di chiamare EF 4.1 specifiche operazioni di accesso ai dati dal dominio. Preferisco chiamare le operazioni generiche CRUD da un'interfaccia IRepository. Ciò renderà i test più facili e se dovessi cambiare il framework di accesso ai dati in futuro, potrò farlo senza dover refactoring un sacco di codice.Schema di repository con Entity Framework 4.1 e relazioni padre/figlio
Ecco un esempio della mia situazione:
Ho 3 tabelle nel database: Group
, Person
e GroupPersonMap
. GroupPersonMap
è una tabella di collegamento e consiste semplicemente nelle chiavi primarie Group
e Person
. Ho creato un modello EF dei 3 tavoli con il designer VS 2010. EF è stato abbastanza intelligente da supporre che GroupPersonMap
sia una tabella di collegamenti in modo che non venga visualizzata nella finestra di progettazione. Voglio utilizzare i miei oggetti dominio esistenti invece delle classi generate da EF, quindi spengo la generazione del codice per il modello.
mie classi esistenti che corrisponde al modello di EF sono i seguenti:
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; }
}
Ho un'interfaccia generica repository in questo 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()
}
e un repository EF generico:
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
}
}
Ora supponiamo di voler mappare uno Group
esistente a unoesistente. Avrei dovuto fare qualcosa di simile al seguente:
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);
Ma questo non funziona perché EF pensa Person
è nuovo, e cercherò di ri- INSERT
il Person
nel database, che causerà un errore di vincolo di chiave primaria . Devo utilizzare il metodo Attach
di Attach
per indicare a EF che il Person
esiste già nel database, quindi è sufficiente creare una mappa tra Group
e Person
nella tabella GroupPersonMap
.
Quindi, al fine di collegare Person
al contesto ora devo aggiungere un metodo Attach
al mio IRepository:
public interface IRepository<T> where T: class
{
// existing methods
T Attach(T entity);
}
Per fissare il primario errore di vincolo di chiave:
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);
fisso. Ora devo occuparmi di un altro problema in cui Group
viene aggiornato nel database ogni volta che creo una mappa di gruppo/persona. Questo perché nel mio metodo EFRepository.Update()
, lo stato dell'entità è impostato esplicitamente su Modified'. I must set the Group's state to
Invariato so the
La tabella di gruppo non viene modificata.
Per correggere questo devo aggiungere una sorta di Update
sovraccarico al mio IRepository che non aggiorna l'entità root, o Group
, in questo caso:
public interface IRepository<T> where T: class
{
// existing methods
T Update(T entity, bool updateRootEntity);
}
L'implentation EF4 del metodo di aggiornamento potrebbe essere qualcosa così:
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();
}
La mia domanda è: mi sto avvicinando a questo nel modo giusto? Il mio repository sta iniziando a sembrare EF centric mentre comincio a lavorare con EF e il pattern del repository. Grazie per aver letto questo lungo post
+1 Ottima risposta –
+1 Molto informativo –
+1 per entità quadro + unità di lavoro. I repository devono condividere lo stesso dbcontext. Scommetto che è quello che è il mio problema. – Jhayes2118