Некоторые мысли о LINQ
От: VladD2 Российская Империя www.nemerle.org
Дата: 30.12.08 18:58
Оценка: 45 (10) +2
Писал тут редакционную статью (все заняты, свалили ответственность на меня) для нашего журнала Технология Клиент-Сервер. В результате получились мысли о LINQ которые мне давно приходили в голову, но времени изложить в форуме не было. Вот что получилось:

Начитавшись в форумах тем, посвященных LINQ и его разновидностям – LINQ to SQL и LINQ to EF (Entity Framework, далее просто EF), я захотел порассуждать на эту тему. Отчасти это желание возникло от того, что я заметил некую терминологическую путаницу, которая сильно влияет на выводы. А отчасти желание подогревается какой-то дикой непоследовательностью в подходе Microsoft.
Итак, разберем все по порядку.
Терминология. На RSDN разгорелся нешуточный флэйм по поводу того, что с точки зрения поклонников Hibernate (и других похожих продуктов, основанных на идеях EJB, которые в свою очередь основаны на идеях CORBA), Microsoft сделала неправильный O/R Mapper (по-русски это будет звучать вроде объектно-реляционные отображальники ). С точки зрения поклонников Hibernate, Microsoft сделал не полноценный O/R Mapper, так как он не предоставляет мощной и законченной подсистемы Persistence Ignorance (PI), а защитники подхода Microsoft отвечают своим оппонентам, что Microsoft вовсе не пытался создать O/R Mapper.

Небольшая справка. PI – это давняя идея, о которой я впервые услышал при знакомстве с CORBA-ой. Ее суть заключается в том, что программист должен работать с «чистой» объектной моделью, а слой персистентности (это изобретение человеческой мысли даже назвать, то по русски трудно) «прозрачно» для программиста хранит данные в БД. При этом программист думает об объекте как о долгоживущей сущности. Объект (в мыслях программиста) живет дольше чем какое либо приложение им манипулирующее, будь то Web-страница или сервер приложений. В общем, программист манипулирует объектом (или их коллекциями), а подсистема персистентности сама когда надо сохраняет его состояние в персистентом хранилище (которым обычно выступает СУБД, но с точки зрения чисто идеологии может выступать все что угодно). Иногда на эту идею смотрят как на «объектизацию» БД, в том смысле, что программист думает, что в БД хранятся объекты (в полном понимании ООП).

Учитывая вышесказанное, можно смело сказать, что под поклонниками Hibernate я имею в виду поклонников PI.
Кто же здесь прав, а кто неправ? Неправы оба лагеря. И неправы они в восприятии понятия O/R Mapper. O/R Mapper не подразумевает реализации каких-то там идей «объектизации» БД и тем более PI. Задача O/R Mapper – просто отобразить данные, получаемые из РСУБД, в объекты, которыми удобно манипулировать в программе. Эту функцию в реализациях LINQ (особенно EF) реализована довольно неплохо. Но и LINQ to SQL, и EF вообще не предназначены для реализации идей персистентности, и в частности, PI. Вместо этого они (точнее, их создатели) пропагандируют старую как мир идею воспринимать модель данных, хранимых в БД и отображаемых на объекты, как ER-модель (Entity Relation). В этой модели данные рассматриваются как набор сущностей, связанных между собой отношениями. Она отлично ложится на РСБУД с их «реляционно-алгеброическим бэкграундом».
Учитывая вышесказанное, довольно глупо ожидать от реализаций LINQ качественной реализации абстракций, для поддержки которых они не предназначены. LINQ просто не должен быть предназначен для обработки объектов путем перебора их в циклах и изменения их свойств. Вместо этого он должен манипулировать наборами сущностей с помощью запросов. Только сущности в LINQ представлены не кортежами, как в РСУБД, а объектами. Вот почему в LINQ to SQL даже нет сложной системы поддержки наследования. Ведь по уму одна сущность должна отображаться на один класс. Это самое прямое отображение, а значит, самое очевидное.
Однако LINQ должен быть удобным. А применение соединения таблиц (join) трудно назвать удобной штукой. Куда логичнее написать нечто order.Details.Sum(d => d.UnitPrice * d.Quantity), нежели возиться с соединениями «таблиц», указывая ключи и прочую чепуху. Тем более что совершенно элементарно реализовать отображение красивой объектно-функциональной нотации в те же самые join-ы.
Вот почему в LINQ-ах есть возможности так похожие на те, что есть в Hibernate. Но у них другая природа и другие задачи. Да, и не спорьте со мной насчет того, что Hibernate тоже может делать запросы. Я знаю. Но у него другие отправные точки в дизайне. Он сначала PI, а уж затем может выполнять запросы на внутреннем языке. А LINQ – это интегрированный в язык язык запросов и ничего большего.
Все вроде бы встало на свои места? Как бы ни так. К сожалению, нормальному восприятию LINQ to SQL и EF как O/R Mapper-а, основанного на идеях ER, мешает тот способ, которым LINQ предлагает вносить изменения в БД. А способ это самый что ни на есть неподходящий для LINQ-а, пропагандирующего подход «ER-моделирования». Чтобы изменить данные в обеих реализациях LINQ нужно получить объект, изменить его свойства, поместить этот объект в специальный контекст (если он еще не находится в нем) и, в конце концов, выполнить у этого контекста процедуру записи. Это тот же самый подход, что используется в Hibernate и ему подобных PI-средах. И нужен этот подход именно потому, и только для того, чтобы скрыть от разработчика реальную – реляционную – суть обрабатываемых данных, и позволить ему возиться с объектами, как будто они являются полноценными (в смысле ООП) объектами, а не отображением ER-сущностей.
На мой взгляд – это жутчайшая непоследовательность и грубейшая ошибка дизайна.
Изменение данных в системах, пропагандирующих ER-подход, должно вестись теми же реляционными средствами, т.е. с помощью операторов insert, update и delete, как в оригинальном SQL, под который пытается мимикрировать LINQ. Тогда все бы встало на свои места. Сущности не нужно было бы прицеплять к контексту. Транзакции можно было бы объявлять явно. А возможности манипуляций данными значительно расширились бы, так как в выражениях insert, update и delete можно было бы применять всю мощь запросов!
В общем, на мой взгляд, Microsoft со своим EF пошла не в ту степь. Она (видимо, поддавшись крикам Hibernate-щиков) пошла по пути превращения EF в клон «классического» O/R Mapper-а, а реально – персистентной подсистемы. На мой взгляд, идея персистентности и манипуляции данными как объектами в корне порочна. Мне казалось, что Microsoft нащупал правильный путь, но сейчас я наблюдаю, как Microsoft твердой поступью идет в обратном направлении.
Единственное, что меня радует – это то, что Microsoft-таки показала, как интегрировать ER-подход с современными объектно-ориентированными языками с функциональным уклоном, и кто-то из посторонних сможет реализовать, казалось бы, такие напрашивающиеся для LINQ insert, update и delete. Жаль, что, в отличие от Nemerle, такие языки, как C# и VB, не позволяют расширить свой синтаксис чтобы бесшовно ввести эти операторы в язык. Но хотя бы в виде процедур, принимающих условия в виде деревьев выражений, их можно добавить в любой язык. Продаю идею .
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.