{season} of code

Мои первые попытки использовать Test Driven Development закончились обычными проблемами с доступом к данным. Почти сразу выяснилось, что:

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

2. Невозможно заменить весь код доступа к данным mock-ами. На самом деле, можно попробовать, но это привязывает тесты к конкретной реализации функционала. Вы вынуждены создавать mock для каждого обращения к DAL. Вы так же вынуждены изменять тесты при рефакторинге, что усложняет разработку сопровождение.

Общепринятое решение этих проблем – использование шаблонов Persistence Ignorance (PI) и Repository. Достаточно погуглить “IQueryable Repository”и вы найдете чуть более 9000 упоминаний чего-то вроде:

interface IRepository

{

    IQueryable<T> All<T>();

    void Insert<T>(T entity);

    void Update<T>(T entity);

    void Delete<T>(T entity);

}

…и множество реализаций этого интерфейса поверх LINQ To SQL. Несмотря на разнообразие, практически все они реализуют все один и тот же тип шаблона Repository - “Plain old CLR objectRepository. Это означает, что на базовый шаблон они накладывают довольно сильные ограничения.

Самое серьезное – то, что объекты должны быть “плоскими” в худшем смысле этого слова. Вы не можете заставить POCO Repository загрузить связи между объектами. Вы не можете написать код вроде:

repository.All<Project>()

    .Where(p => p.Tasks.Count > 10);

В мире POCO связей вообще не существует! Одну короткую строчку кода вам придется заменить вот этим:

var projectIds = repository.All<Task>()

    .GroupBy(t => t.ProjectId)

    .Where(gr => gr.Count() > 10)

    .Select(gr => gr.Key)

    .ToList();

 

repository.All<Project>()

    .Where(p => projectIds.Contains(p.Id));

Даже такой простой пример раздувается до двух запросов, с использованием IN. Написав такой мегакод пару раз, я решил что нужно найти способ запихнуть этот запрос назад в одну строчку. И при этом сохранить поддержку Persistence Ignorance.

Осталось придумать лишь еще одну реализацию шаблона Repository, которая:

1. Полностью поддерживает связи (Associations). Она должна поддерживать выполнение кода вроде:

repository.All<Task>()

    .Where(t =>

            t.Project.Customer.Name.StartsWith("a"));

2. Поддерживает загрузку связанных объектов (Deep Load). Она должна уметь загружать одиночные и множественные зависимости по требованию.

// задачи, со свойством Project установленным в  null

repository.All<Task>();

 

// задачи с загруженным свойством Project

repository.All<Task>()

    .LoadWith(t => t.Project);

 

// задачи в будущих проектах

// свойство Project не загружено (null)

repository.All<Task>()

    .Where(t => t.Project.StartDate > DateTime.Today);

3. Поддерживает перехват запросов (Query Interception), для красивых глобальных фильтров

// задачи видимые текущему пользователю

// поведение по умолчанию

repository.All<Task>();

 

// все задачи, без учета прав пользователя

repository.All<Task>()

    .IgnorePermissions();

 

// сообщения со статусом != Deleted

// поведение по умолчанию

repository.All<Message>();

 

// только “удаленные” сообщения

repository.All<Message>()

    .Deleted();

 

// все сообщения

repository.All<Message>()

    .IncludeDeleted();       

4. Позволяет легко переключиться между LINQ To SQL и In-Memory реализациями, одним ключом в конфиге. Изначально ориентирована на использование IoC/DI.

Мы попытались решить эти проблемы и сделать “старые плоские” объекты не настолько плоскими и старыми. Код и примеры выложены на  http://lsda.codeplex.com/. Этот блог будет постепенно расти до полноценной документации :)

Отзывы, пожелания и патчи приветствуются.

9 Responses to “IQueryable Repository – Что дальше?”

  1. build_your_web

    Жаль что вы только начали.
    Как раз ищу подобную библиотеку.

  2. PashaPash

    2build_your_web: начали - понятие осносительное. мы на нем уже штук 5 проектов сдали.
    Попробуй забрать source code с кодеплекса, там есть базовые примеры. Постараюсь до конца недели зарелизить и выложить более полные семплы.

  3. xumix

    а где можно посомтреть конкретные примеры, где видно преимущества Repository?

  4. free oboi

    Для более подробного и внимательного изучения добавил в избранное. Буду изучать

  5. Zidlirm

    и всё эе: неподражаемо.. а82ч

  6. Interracial Peeing

    ehh.. good thread ))

  7. Artemiy

    Спасибо, очень понравилось

  8. Драйв

    Хорошая статья. Действительно было интересно почитать. Не часто такое и встречается та.Наверное стоит подписаться на ваше RSS

  9. WebMoney

    Действительно интересно. Побольше бы таких статей.

Leave a Reply

Proudly powered by WordPress. Theme developed with WordPress Theme Generator.
Copyright © {season} of code. All rights reserved.