Шаблон репозитория с Entity Framework 4.1 и отношения родитель / потомок

У меня все еще есть некоторая путаница с шаблоном репозитория. Основная причина, по которой я хочу использовать этот шаблон, заключается в том, чтобы избежать вызова специальных операций доступа к данным EF 4.1 из домена. Я бы предпочел вызывать общие операции CRUD из интерфейса IRepository. Это упростит тестирование, и если мне когда-нибудь придется изменить структуру доступа к данным в будущем, я смогу сделать это без рефакторинга большого количества кода.

Вот пример моей ситуации:

У меня есть В базе данных 3 таблицы: Group , Person и GroupPersonMap . GroupPersonMap представляет собой таблицу ссылок и состоит только из первичных ключей Group и Person . Я создал EF-модель из трех таблиц с дизайнером VS 2010. 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
    }
}

Теперь предположим, что я просто хочу отобразить существующий Группа существующему человеку . Мне пришлось бы сделать что-то вроде следующего:

        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 новым и попытается повторно - INSERT Person в базу данных, что вызовет ошибку ограничения первичного ключа. Я должен использовать метод DbSet Attach , чтобы сообщить EF, что 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 ОБНОВЛЯЕТСЯ в базе данных каждый раз, когда я создаю карту Group / Person. Это связано с тем, что в моем методе EFRepository.Update () состояние объекта явно установлено на Modified '. Я должен установить состояние группы на Без изменений , чтобы таблица Group` не изменялась.

Чтобы исправить это, я должен добавить какую-то перегрузку 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 и шаблоном репозитория. Спасибо, что прочитали этот длинный пост

35
задан jodev 24 August 2011 в 08:03
поделиться