Під час співбесіди від мене попросили пояснити, чому схема сховища не є гарною схемою для роботи з ОРМ, як Entity Framework. Чому це так?
Під час співбесіди від мене попросили пояснити, чому схема сховища не є гарною схемою для роботи з ОРМ, як Entity Framework. Чому це так?
Відповіді:
Я не бачу жодної причини, щоб шаблон репозиторію не працював з Entity Framework. Шаблон сховища - це рівень абстракції, який ви розміщуєте на рівні доступу до даних. Вашим шаром доступу до даних може бути все - від чистих процедур, що зберігаються в ADO.NET, до Entity Framework або файлу XML.
У великих системах, де у вас є дані, що надходять з різних джерел (база даних / XML / веб-сервіс), добре мати рівень абстракції. Шаблон сховища добре працює в цьому сценарії. Я не вірю, що Entity Framework є достатньою абстракцією, щоб приховати те, що відбувається за лаштунками.
Я використовував шаблон репозиторію з Entity Framework в якості методу рівня доступу до даних, і я ще не стикаюся з проблемою.
Ще одна перевага абстрагування DbContext
репозиторію - тестування одиниць . Ви можете мати свій IRepository
інтерфейс, до якого є 2 реалізації, одна (справжня сховище), яка використовує DbContext
для спілкування з базою даних, і друга, FakeRepository
яка може повертати об'єкти пам'яті / знущаються дані. Це робить ваш IRepository
блок опробовуваним, таким чином, інші частини коду, які використовуються IRepository
.
public interface IRepository
{
IEnumerable<CustomerDto> GetCustomers();
}
public EFRepository : IRepository
{
private YourDbContext db;
private EFRepository()
{
db = new YourDbContext();
}
public IEnumerable<CustomerDto> GetCustomers()
{
return db.Customers.Select(f=>new CustomerDto { Id=f.Id, Name =f.Name}).ToList();
}
}
public MockRepository : IRepository
{
public IEnumerable<CustomerDto> GetCustomers()
{
// to do : return a mock list of Customers
// Or you may even use a mocking framework like Moq
}
}
Тепер, використовуючи DI, ви отримуєте реалізацію
public class SomeService
{
IRepository repo;
public SomeService(IRepository repo)
{
this.repo = repo;
}
public void SomeMethod()
{
//use this.repo as needed
}
}
Єдиний найкращий привід не використовувати шаблон репозиторію з Entity Framework? Entity Framework вже реалізує шаблон репозиторію. DbContext
- це ваш UOW (Unit of Work) і кожен DbSet
- сховище. Нанесення іншого шару поверх цього не тільки є зайвим, але ускладнює обслуговування.
Люди дотримуються шаблонів, не усвідомлюючи мети цього шаблону. У випадку шаблону репозиторію метою є абстрагування логіки запитів бази даних низького рівня. За старих часів фактичного запису SQL-заяв у свій код, шаблон репозиторію був способом перемістити цей SQL з окремих методів, розпорошених по вашій кодовій базі та локалізувати його в одному місці. Наявність такої ORM, як Entity Framework, NHibernate тощо, є заміною цього абстрагування коду, і як таке відміняє потребу в шаблоні.
Однак, це не погана ідея створити абстракцію поверх вашої ОРМ, просто не так складно, як UoW / репозиторія. Я б сформулював схему обслуговування, де ви створюєте API, який може використовувати ваша програма, не знаючи і не піклуючись про те, чи надходять дані з Entity Framework, NHibernate або веб-API. Це набагато простіше, оскільки ви просто додаєте методи до свого класу обслуговування, щоб повернути дані, які потрібні вашій програмі. Якщо ви, наприклад, писали додаток "Зобов’язання", у вас може виникнути службовий дзвінок щодо повернення предметів, які належать до цього тижня та ще не завершені. Все, що ваша програма знає, це те, що якщо вона хоче цю інформацію, вона називає цей метод. Всередині цього методу та у вашій службі взагалі ви взаємодієте з Entity Framework або будь-яким іншим, чим ви користуєтесь. Потім, якщо пізніше ви вирішите переключити ORM або витягнути інформацію з веб-API,
Це може здатися, що це потенційний аргумент для використання шаблону репозиторію, але ключова відмінність тут полягає в тому, що сервіс - це тонший шар і спрямований на повернення повністю виправлених даних, а не на те, до чого ви продовжуєте запитувати, як, наприклад, з сховище.
DbContext
з EF6 + (див.: Msdn.microsoft.com/en-us/data/dn314429.aspx ). Навіть в менших версіях, ви можете використовувати підроблені DbContext
-like клас з знущався DbSet
з, так як DbSet
реалізує iterface, IDbSet
.
Ось один взяття від Айенде Рахієн: Архітектура в ямі дум: Зло шару абстракції сховища
Я ще не впевнений, чи згоден я з його висновком. Це привід 22 - з одного боку, якщо я обертаю свій контекст EF у сховищах, характерних для типу методами пошуку даних, специфічних для запитів, я фактично можу перевірити свій код (тип), що майже неможливо з Entity Тільки рамки. З іншого боку, я втрачаю здатність робити багатий запит і семантичне підтримання відносин (але навіть коли я маю повний доступ до цих функцій, я завжди відчуваю, що ходжу по яєчним шкаралупам навколо EF або будь-який інший ORM, який я можу вибрати , оскільки я ніколи не знаю, якими методами його реалізація IQueryable може чи не може підтримувати, чи інтерпретуватиме моє додавання до колекції властивостей навігації як створення або просто асоціацію, чи буде це ледачий чи прагнутий навантаження чи взагалі не завантажується за замовчуванням тощо, тому, можливо, це на краще. Об'єктно-реляційне об'єктивне "відображення" з нульовим імпедансом - це щось міфологічне створіння - можливо, саме тому останній випуск Entity Framework був кодований під назвою "Чарівний Єдиноріг").
Однак пошук ваших організацій за допомогою методів пошуку даних, що стосуються запитів, означає, що ваші тестові одиниці тепер по суті є тестами білого поля, і ви не маєте вибору в цьому питанні, оскільки ви повинні заздалегідь знати, до якого методу репозиторію збирається тестуваний підрозділ. дзвоніть, щоб знущатися з нього. І ви все ще фактично не тестуєте самі запити, якщо ви також не пишете інтеграційні тести.
Це складні проблеми, які потребують комплексного вирішення. Ви не можете це виправити, просто зробивши вигляд, що всі ваші сутності є окремими типами, що не мають між собою взаємозв'язків, і розмежувати їх у своє сховище. Ну можна , але це смокче.
Оновлення: Я мав певний успіх у використанні постачальника зусиль для Entity Framework. Зусилля - це провайдер пам'яті (відкритий код), який дозволяє використовувати EF у тестах саме так, як ви використовували б його щодо реальної бази даних. Я розглядаю можливість переключення всіх тестів у цьому проекті, над яким я працюю, щоб скористатися цим провайдером, оскільки, здається, все набагато простіше. Це єдине рішення, яке я знайшов до цих пір, яке стосується всіх проблем, про які я раніше шумував. Єдине, що при запуску моїх тестів є невелика затримка, оскільки для створення бази даних в пам'яті (для цього використовується інший пакет під назвою NMemory), але я не вважаю це справжньою проблемою. Там є стаття Code Code, яка розповідає про використання Effort (порівняно з SQL CE) для тестування.
DbContext
. Незважаючи на те, ви завжди можете знущатися DbSet
, і це все-таки м'ясо Entity Framework. DbContext
це трохи більше, ніж клас для розміщення ваших DbSet
властивостей (сховищ) в одному місці (одиниця роботи), особливо в контексті тестування одиниць, де всі ініціалізація бази даних та з'єднання не потрібні і не потрібні.
Причина, чому ви, ймовірно, зробите це, тому що це трохи зайве. Entity Framework надає вам багато переваг кодування та функціональних переваг, саме тому ви використовуєте його, якщо потім взяти це і загорнути в шаблон сховища, ви викидаєте ці переваги, можливо, ви також можете використовувати будь-який інший рівень доступу до даних.
Теоретично я вважаю, що має сенс інкапсулювати логіку підключення до бази даних, щоб зробити її більш легкою для повторного використання, але, як стверджує посилання нижче, наші сучасні рамки по суті дбають про це зараз.
ISessionFactory
і ISession
вони легко піддаються гнучкості), але DbContext
, на жаль , це не так просто
Дуже вагомою причиною для використання шаблону репозиторію є дозвіл відокремити вашу логіку бізнесу та / або інтерфейс користувача від System.Data.Entity. Цьому є чимало переваг, включаючи реальні переваги при тестуванні одиниць, дозволяючи йому використовувати Fakes або Mocks.
У нас виникли проблеми з повторюваними, але різними випадками DbContext Entity Framework DbContext, коли контейнер IoC, який створює нові () репозиторії кожного типу (наприклад, UserRepository та GroupRepository, що кожного виклику власного IDbSet з DBContext), іноді може викликати кілька контекстів на запит (у контексті MVC / веб).
Більшу частину часу він все ще працює, але коли ви додаєте службовий рівень поверх цього, і ті служби припускають, що об’єкти, створені з одним контекстом, будуть правильно приєднані як дочірні колекції до нового об'єкта в іншому контексті, він іноді виходить з ладу, а іноді і не зникає ' t залежно від швидкості комітів.
Після випробування шаблону репозиторію на невеликому проекті я настійно раджу не використовувати його; не тому, що це ускладнює вашу систему, і не тому, що глузування з даних є кошмаром, а тому, що тестування стає марним !!
Дані глузування дозволяють додавати деталі без заголовків, додавати записи, що порушують обмеження в базі даних, та видаляти об'єкти, які БД відмовиться видаляти. У реальному світі одне оновлення може впливати на кілька таблиць, журналів, історії, резюме тощо, а також на стовпці, такі як поле останньої модифікованої дати, автоматично створені ключі, обчислені поля.
Коротко кажучи, ваш тест на реальній базі даних дає вам реальні результати, і ви можете перевірити не тільки ваші сервіси та інтерфейси, а й поведінку бази даних. Ви можете перевірити, чи зберігаються ваші збережені процедури з даними, повернути очікуваний результат чи справді видалена запис, який ви надіслали для видалення! Такі тести також можуть піддавати такі проблеми, як забуття підняти помилки зі збереженої процедури та тисячі таких сценаріїв.
Я думаю, що структура структури реалізує шаблон репозиторію краще, ніж будь-яка з статей, які я прочитав до цього часу, і це виходить далеко за рамки того, що вони намагаються виконати.
Репозиторій був найкращою практикою в ті дні, коли ми використовували XBase, AdoX та Ado.Net, але з сутністю !! (Сховище над сховищем)
Нарешті, я думаю, що занадто багато людей вкладають багато часу на вивчення та реалізацію схеми сховищ, і вони відмовляються її відпускати. Переважно доводити собі, що вони не витрачали свій час.
Це пов'язано з міграцією: неможливо змусити переходити до роботи, оскільки рядок з'єднання знаходиться в web.config. Але, DbContext знаходиться в шарі сховища. IDbContextFactory повинен мати конфігураційний рядок до бази даних. Але немає можливості, щоб міграція отримувала рядок з'єднання з web.config.
Є робота навколо, але я ще не знайшов чистого рішення для цього!