Шаблон репозитория с EF4 CTP5 - PullRequest
       17

Шаблон репозитория с EF4 CTP5

5 голосов
/ 21 февраля 2011

Я пытаюсь реализовать шаблон хранилища с ef4 ctp5, я что-то придумал, но я не эксперт в ef, поэтому Я хочу знать, хорошо ли то, что я сделал.

это мой контекст БД

public class Db : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Role> Roles { get; set; }
}

и хранилище: (упрощенно)

public class Repo<T> : IRepo<T> where T : Entity, new()
{
        private readonly DbContext context;

        public Repo()
        {
            context = new Db();
        }

        public IEnumerable<T> GetAll()
        {
            return context.Set<T>().AsEnumerable();
        }

        public long Insert(T o)
        {
            context.Set<T>().Add(o);
            context.SaveChanges();
            return o.Id;
        }
}

Ответы [ 2 ]

10 голосов
/ 21 февраля 2011

Вы должны сделать шаг назад и подумать о том, что должен делать репозиторий. Хранилище используется для извлечения записей, добавления записей и обновления записей. Репозиторий, который вы создали, едва обрабатывает первый случай, обрабатывает второй случай, но не эффективно, и совсем не обрабатывает третий случай.

Большинство универсальных репозиториев имеют интерфейс, аналогичный

public interface IRepository<T> where T : class
{
    IQueryable<T> Get();
    void Add(T item);
    void Delete(T item);
    void CommitChanges();
}

Для извлечения записей вы не можете просто вызвать весь набор с помощью AsEnumerable(), потому что это загрузит каждую запись базы данных для этой таблицы в память. Если вам нужны только пользователи с именем пользователя username1, вам не нужно загружать каждого пользователя для базы данных, так как это приведет к значительному снижению производительности базы данных и значительному снижению производительности клиента без какой-либо выгоды.

Вместо этого, как вы увидите из интерфейса, который я разместил выше, вы хотите вернуть IQueryable<T> объект. IQuerable разрешают любому классу, вызывающему хранилище, использовать Linq и добавлять фильтры к запросу базы данных, и после запуска IQueryable он полностью запускается в базе данных, получая только нужные записи. База данных гораздо лучше сортирует и фильтрует данные, чем ваши системы, поэтому лучше всего делать на БД как можно больше.

Теперь, что касается вставки данных, у вас есть правильная идея, но вы не хотите немедленно вызывать SaveChanges(). Причина в том, что лучше всего вызывать Savechanges() после того, как все ваши операции с БД были поставлены в очередь. Например, если вы хотите создать пользователя и его профиль в одном действии, вы не можете использовать свой метод, потому что каждый вызов Insert будет вызывать вставку данных в базу данных.

Вместо этого вам нужно разделить вызов Savechanges() на метод CommitChanges, который я описал выше.

Это также необходимо для обработки данных обновления в вашей базе данных. Чтобы изменить данные сущности, Entity Framework отслеживает все полученные им записи и следит за ними, чтобы увидеть, были ли внесены какие-либо изменения. Тем не менее, вы все равно должны сказать Entity Framework, что нужно отправить все измененные данные в базу данных. Это происходит с вызовом context.SaveChanges(). Следовательно, вам нужно, чтобы это был отдельный вызов, чтобы вы могли фактически обновлять отредактированные данные, которые ваша текущая реализация не обрабатывает.


Edit: Ваш комментарий заставил меня осознать еще одну проблему, которую я вижу. Одним из недостатков является то, что вы создаете контекст данных внутри репозитория, и это не хорошо. Вы действительно должны иметь все (или большинство) ваших созданных репозиториев, совместно использующих один и тот же экземпляр вашего контекста данных.

Entity Framework отслеживает, в каком контексте отслеживается объект, и будет исключением, если вы попытаетесь обновить объект в одном контексте с другим. Это может произойти в вашей ситуации, когда вы начинаете редактировать объекты, связанные друг с другом. Это также означает, что ваш SaveChanges() вызов не является транзакционным, и каждый объект обновляется / добавляется / удаляется в своей собственной транзакции, что может привести к путанице.

Мое решение этого в моих репозиториях заключается в том, что DbContext передается в репозиторий в конструкторе.

6 голосов
/ 15 марта 2011

Возможно, за это проголосуют, но DbContext уже является хранилищем.Когда вы представляете свои доменные модели как свойства коллекции вашего конкретного DbContext, тогда EF CTP5 создает для вас хранилище.Он представляет собой интерфейс, похожий на коллекцию, для доступа к моделям доменов, а также позволяет передавать запросы (как linq или spec-объекты) для фильтрации результатов.

Если вам нужен интерфейс, CTP5 не предоставляет его для вас.,Я обернул свой собственный вокруг DBContext и просто выставил общедоступные члены из объекта.Это адаптер для тестируемости и DI.

Я прокомментирую для разъяснения, если то, что я сказал, очевидно не очевидно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...