metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2010-08-12 11:49 am

Опять кодогенерация, шаблонизаторы vs AST

Кодогенератор-шаблонизатор T4 оказался весьма удобной вещью, на которой кодогенераторы пишутся и расширяются просто со страшной скоростью.

И поначалу это гораздо понятнее, чем кодогенератор с использованием AST целевого языка и претти-принтингом оного AST в исходники.
Но есть одно НО.
Результат без подсветки синтаксиса и автоформатирования к поддержке практически непригоден. Исходник представляет собой смесь C# кода, который выполняется и C# кода который генерируется, с разделителями вида <#,<#=,<#+ и #>. Без заточенного под это дело редактора после определенного уровня сложности кодогенератора в этом понять ничего невозможно.

Например, я, упившись крепким чаем, в припадке безумия таки реализовал свою старую идею - генерацию из простой модели иммутабельных сущностей и методов сохранения и загрузки оных сущностей в простой XML, где полям-значениям соответствуют атрибуты, полям-объектам - элементы, полям-спискам(List<>) - элементы с вложенными списками. Т.е. вариант XML, в котором это дело наиболее читабельно глазами. Если не боитесь пауков - можете глянуть на этот мрак.
По мере расширения этого кодогенератора (простые сущности-записи -> сущности с полями-списками -> поддержка enum -> поддержка полей-вложенных объектов -> поддержка memo-полей (строки в которых длинный текст с переносами)) это все понемногу превратилось в жопный ад. 10 кб текста, 300 строк, в которых даже я с трудом ориентируюсь уже.
Использовать это очень просто, расширять дальше - уже сложно.

Кроме того, во всей этой кодогенерации очень бесит необходимость постоянно расширять систему типов. Т.к. один и тот же string в целевой проге - это может быть и коротенькое имя, и текст SQL запроса и RTF-документ. А при показе поля с датой пользователю может быть необходимо использовать culture-invariant формат (например я для служебных данных и для себя пользуюсь везде только им, чтобы не вспоминать "на какой локали я сижу") а может и формат из текущей локали.
В общем, если делать классическими дотнетовскими средствами, как положено, то на каждое поле или проперть в POCO-сущности навешивается еще 5-10 атрибутов, и по всему коду расползается анализ этих атрибутов, т.е. фактически мало того, что мы расширяем систему типов дополнительными описаниями, так еще и всю их обработку делаем в рунтайме, как с какой-то динамической типизацией.



PS: Во, вспомнил еще идею, которая мне насчет T4 в голову пришла.
Частенько в C# не хватает ADT, но их можно сымитировать, сделав базовый класс и унаследовав от него все варианты ADT с нужными полями. Чтобы сделать pattern matching поверх этого, нужно написать в базовом классе метод, в который передается в качестве параметров по одному делегату на каждый вариант ADT и он анализирует текущий тип объекта, вызывая соответствующий делегат, подставляя поля варианта в качестве параметров делегату.

Руками такое писать очевидно влом. Но можно сделать на T4 мелкий шаблончик, который из описания ADT в привычном виде сгенерит все классы, конструктора, прототипы делегатов и метод для pattern matching.

[identity profile] metaclass.livejournal.com 2010-08-12 04:32 pm (UTC)(link)
И таки да, это связка СУБД и DataTable

[identity profile] norguhtar.livejournal.com 2010-08-12 04:34 pm (UTC)(link)
У меня дурацкий вопрос, а чем ORM не ку? Не умеет сложных структур данных?

[identity profile] metaclass.livejournal.com 2010-08-12 04:45 pm (UTC)(link)
Ну можно сгенерить объекты и маппинги NHibernate для них, особо это ничего не изменит - та же модель, другой способ хранения данных (вместо стандартной DataTable, какая-нибудь вариация на тему List) вместо метаданных отчета - атрибуты на пропертях сущности.
А, ну еще вместо нормального SQL - его вариация из ORM, тем более что оно все равно не умеет код генерить, поэтому там извраты будут.
Я лучше LINQToSQL прикручу, что ли.

Конкретно в данном случае это все нужно только для того, чтобы из модели сгенерить все чохом - базу, запросы, пользовательские подписи, описание редакторов для записей, итд. Это чисто для table-centric гуя.

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

[identity profile] norguhtar.livejournal.com 2010-08-12 05:24 pm (UTC)(link)
Более менее понял. Как бе у меня MVC так-как используется решение идет от кручения того что надо получить в controller и создания view. Но view видимо реже у меня меняется, чем у вас таблица.

[identity profile] metaclass.livejournal.com 2010-08-12 05:31 pm (UTC)(link)
Таблицы меняются достаточно часто, чтобы поддержание в актуальном виде всего что от них зависит было очень напряжно.
В общем, если от заказчиков добиваться строгого ТЗ и работать исключительно по нему, а так же пропускать изменения через все круги бюрократических согласований - работа, в основном, просто встанет.