User Tools

Site Tools


developer:linq

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
developer:linq [2011/05/05 11:43]
celutp
developer:linq [2011/09/14 12:17] (current)
celutp
Line 11: Line 11:
 ===== Предварительная настройка =====      ===== Предварительная настройка =====     
   * В [[admin:​config|конфигурационный файл Backend]] нужно добавить параметр **SqlMetalPath**,​ определяющий путь к стандартной утилите генерации SQL-классов. Данная утилита устанавливатся вместе с Visual Studio 2008. Путь к ней может выглядеть так: ''​C:​\Program Files\Microsoft SDKs\Windows\v6.0A\bin\sqlmetal.exe''​. Если на целевом компьютере не установлена Visual Studio, утилиту можно просто [[http://​downloads.quantumart.ru/​sqlmetal.zip|скачать]].   * В [[admin:​config|конфигурационный файл Backend]] нужно добавить параметр **SqlMetalPath**,​ определяющий путь к стандартной утилите генерации SQL-классов. Данная утилита устанавливатся вместе с Visual Studio 2008. Путь к ней может выглядеть так: ''​C:​\Program Files\Microsoft SDKs\Windows\v6.0A\bin\sqlmetal.exe''​. Если на целевом компьютере не установлена Visual Studio, утилиту можно просто [[http://​downloads.quantumart.ru/​sqlmetal.zip|скачать]].
-  * Необходимо дать право доступа //Modify// пользователю NETWORK SERVICE на папку //​App_Code//​ для возможности генерации файлов классов и папку //​App_Data//​ для генерации промежуточных файлов отображений.+  * Необходимо дать право доступа //Modify// пользователю, под которым запущен Application Pool (обычно это ​NETWORK SERVICE), на папку //​App_Code//​ для возможности генерации файлов классов и папку //​App_Data//​ для генерации промежуточных файлов отображений.
   * В процессе работы утилита **SQLMetal** может выдавать ошибки,​ которые можно посмотреть в файле //​sqlmetal.log//​ в папке //​App_Data//​. Ошибки обычно связаны с конфликтами имен.   * В процессе работы утилита **SQLMetal** может выдавать ошибки,​ которые можно посмотреть в файле //​sqlmetal.log//​ в папке //​App_Data//​. Ошибки обычно связаны с конфликтами имен.
   * Если на сайте не использовался LINQ-to-SQL,​ то необходимо добавить в //​web.config//​ ссылки на соответствующие сборки:​   * Если на сайте не использовался LINQ-to-SQL,​ то необходимо добавить в //​web.config//​ ссылки на соответствующие сборки:​
Line 41: Line 41:
 Сборка контентов QP7 в классы //​LINQ-to-SQL//​ может осуществляться в двух режимах:​ Сборка контентов QP7 в классы //​LINQ-to-SQL//​ может осуществляться в двух режимах:​
  
-  - С использованием БД. При этом будут использованы специальные настройки LINQ-to-SQL,​ которые задаются в свойствах контентов и полей. +  - [[developer:​linq:​db_mapping|С использованием БД]]. При этом будут использованы специальные настройки LINQ-to-SQL,​ которые задаются в свойствах ​сайта, ​контентов и полей. 
-  - С использованием ​[[developer:​linq:​mapping| ​пользовательского файла отображения]]. При этом файл может быть как составлен разработчиком вручную,​ так и сгенерирован бэкендом по умолчанию.+  - [[developer:​linq:​mapping|С использованием пользовательского файла отображения]]. При этом файл может быть как составлен разработчиком вручную,​ так и сгенерирован бэкендом по умолчанию.
  
 Переключение режимов осуществляется опцией **Использовать прямое отображение из базы данных** (**Use direct mapping from database**) на [[:​site_configuration#​Настройки_сборки_в_LINQ|уровне сайта]]. Cама сборка запускается кнопкой **Собрать Контенты** (**Assemble Contents**) на странице **Контенты** (**Contents**). При этом в папке //​App_Code//​ происходит полная перегенерация всех LINQ-классов текущего сайта. Следует иметь в виду, что данный процесс с большой долей вероятности приведет к перегрузке домена приложения,​ поэтому его стоит с осторожностью применять на продукционных сайтах. Переключение режимов осуществляется опцией **Использовать прямое отображение из базы данных** (**Use direct mapping from database**) на [[:​site_configuration#​Настройки_сборки_в_LINQ|уровне сайта]]. Cама сборка запускается кнопкой **Собрать Контенты** (**Assemble Contents**) на странице **Контенты** (**Contents**). При этом в папке //​App_Code//​ происходит полная перегенерация всех LINQ-классов текущего сайта. Следует иметь в виду, что данный процесс с большой долей вероятности приведет к перегрузке домена приложения,​ поэтому его стоит с осторожностью применять на продукционных сайтах.
Line 48: Line 48:
 Кроме этого, на [[:​site_configuration#​Настройки_сборки_в_LINQ|уровне сайта]] существует опция **Импортировать файл отображения в базу данных**(**Import mapping file to database**),​ которая позволяет импортировать существующий [[developer:​linq:​mapping|пользовательский файл отображения]] в базу данных для последующей настройки через бэкенд QP7 и генерации файла отображения в автоматическом режиме. При успешном выполнении импорта данная опция автоматически сбрасывается,​ а опция **Использовать прямое отображение из базы данных** (**Use direct mapping from database**) автоматически устанавливается. ​ Кроме этого, на [[:​site_configuration#​Настройки_сборки_в_LINQ|уровне сайта]] существует опция **Импортировать файл отображения в базу данных**(**Import mapping file to database**),​ которая позволяет импортировать существующий [[developer:​linq:​mapping|пользовательский файл отображения]] в базу данных для последующей настройки через бэкенд QP7 и генерации файла отображения в автоматическом режиме. При успешном выполнении импорта данная опция автоматически сбрасывается,​ а опция **Использовать прямое отображение из базы данных** (**Use direct mapping from database**) автоматически устанавливается. ​
  
-Опция **Импортировать файл отображения в базу данных**(**Import mapping file to database**) также может быть полезна для первичного заполнения настроек LINQ-to-SQL для существующего сайта с большим количеством контентов. Для этого сначала генерируется ​[[developer:​linq:​mapping| ​пользовательский файл отображения]] по умолчанию (при отключенной опции **Использовать прямое отображение из базы данных** (**Use direct mapping from database**),​ а затем проводится его импорт.+Опция **Импортировать файл отображения в базу данных**(**Import mapping file to database**) также может быть полезна для первичного заполнения настроек LINQ-to-SQL для существующего сайта с большим количеством контентов. Для этого сначала генерируется пользовательский файл отображения по умолчанию (при отключенной опции **Использовать прямое отображение из базы данных** (**Use direct mapping from database**),​ а затем проводится его импорт.
  
 +Генерация LINQ-to-SQL классов и вспомогательных файлов осуществляется в папки //​App_Data//​ и //​App_Code//​ сайта. Расположение этих папок вычисляется из расположения папки //bin//, которая настраивается в [[:​site_configuration#​папка_для_net-сборок|свойствах сайта]]. При этом предполагается,​ что папки //​App_Data//​ и //​App_Code//​ находятся на том же уровне в дереве папок, что и //​bin//​. ​
  
-==== Настройка отображения с использованием БД ==== 
  
-=== Настройки уровня сайта ​==+===== Использование сгенерированных LINQ-to-SQL классов ​=====
-  ***Имя строки ​подключения** (**Connection string name**) -- Название строки соединения ​в секции //​connectionStrings//​ файла //​web.config//​ сайта. По умолчанию - //​qp_database//​. +
-  ***Заменять URL'​ы** (**Replace URLs**) -- Включает замену плейсхолдеров ''<​%=site_url%>''​ и ''<​%=upload_url%>''​ для полей типа //String//, //​VisualEdit//​ и //​Textbox//​. Замена работает в обе стороны,​ как при загрузке, так и при сохранении. Включение этой опции позволяет LINQ-классам поддерживать стандартное поведение Publishing Container'​ов.  +
-  ***Использовать длинные URL'​ы** (**Use long URLs**) -- Модифицирует предыдущую опцию так, что замена будет проводиться на абсолютные URL, а не на относительные (по умолчанию). [[developer:​web_config|Аналогичная опция]] есть для Publishing Container'​ов. +
-  ***Пространство имен для генерируемых классов** (**Namespace for generated classes**) -- Пространство имен, в котором будут сгенерированы ​LINQ-классы. По умолчанию ​пустое. +
-  ***Имя контекстного ​класса** (**Context Class Name**) -- Имя контекстного класса. По умолчанию - //​QPDataContext//​. +
-  ***Выполнять генерацию,​ независимую от БД** (**Proceed DB-independent generation**) -- Включение данной опции позволяет генерировать код, переносимый между различными БД. В этом случае сопоставление между сущностными классами и таблицами БД, осуществляется с помощью специального .map-файла,​ который также генерируется в этом режиме. Имя .map-файла совпадает с именем контекстного класса. +
-  ***Генерировать только .map-файл** (**Generate only .map file**) -- Модификация предыдущей опции. Нужна в случае выполнения на данной БД переносимого кода, который был сгенерирован на другой БД. Таким образом,​ от данной БД требуется только сопоставление между сущностными классами и таблицами БД.+
  
-=== Настройки уровня контента === +  ​[[developer:​linq:​context|Использование контекстного класса]] 
-  ***Отображать как класс** (**Map as class**) ​-- Генерация класса для данного контента производится только при включенной опции. +  - [[developer:​linq:​M2M|Работа с M2M-полями]] 
-  ***Имя (единственное)** (**Name (singular)**) -- Допустимое в С# имя контента в единственном числе. ​Используется как название класса. +  ​[[developer:​linq:​container|Поддержка поведения Publishing Container]] 
-  ***Имя (множественное)** (**Name (plural)**) -- Допустимое в С# имя контента во множественном числе. Используется как название свойства ​контекстного класса, с которого начинается построение LINQ-to-SQL запроса.  +  - [[developer:​linq:​service_field|Поддержка служебных полей контента]] 
-  ***Использовать фильтрацию по умолчанию** (**Use default filtration**) ​-- Включение данной опции необходимо ​для реализации стандартного ​поведения Publishing Container ​(поддержка расписания публикации,​ workflow, архива). В предыдущих версиях реализовалось через вызов метода расширения **ForFrontEnd()**. В текущей версии метод оставлен для обратной совместимости,​ но в случае включенной фильтрации по умолчанию никаких действий не выполняет.  При включенной опции LINQ-класс ​отображается ​не на таблицу CONTENT_NNN,​ а на представление CONTENT_NNN_LIVE (для stage-режима используется аналогичное представление CONTENT_NNN_STAGE вместо CONTENT_NNN_UNITED. +  ​[[developer:​linq:​image|Дополнительные свойства для полей-изображений]] 
-  +  - [[developer:​linq:​caching|Кэширование]] 
-=== Настройки уровня поля === +  - [[developer:linq:​components|Создание компонентов]] 
-  ***Отображать как LINQ-свойство** (**Map as LINQ Property**) -- Генерация ​свойства для ​данного ​поля производится только при включенной опции. +  - [[developer:​linq:​errors|Основные ошибки использования LINQ-to-SQL ​классов]]
-  ***Имя LINQ-свойства** (**LINQ Property Name**) -- Допустимое в С# имя поля. Используется как название свойства. +
-  ​***Имя обратного LINQ-свойства** (**LINQ Back Property Name**) -- Допустимое в С# имя обратного поля для O2M-связи (аналога в QP7 нет).  +
-  ​ +
-Для M2M-связей доступны дополнительные опции: +
-  ***Отображать узловую таблицу как класс** (**Map Junction Table as Class**) -- Генерация класса для ​данной узловой таблицы производится только при включенной опции +
-  ***Имя узлового LINQ-класса (единственное)** (**LINQ Junction Class Name (singular)**) -- Допустимое в С# имя узловой таблицы,​ хранящей данные M2M-связи,​ в единственном числе. Используется как название класса. +
-  ​***Имя узлового LINQ-класса (множественное)** (**LINQ Junction Class Name (plural)**) -- Допустимое в С# имя узловой таблицы, хранящей данные M2M-связи, во множественном числе. Используется как название свойства контекстного класса,​ с которого начинается построение ​LINQ-to-SQL ​запроса.+
  
- +===== Пример добавления статьи =====
- +
-===== Особенности механизма генерации LINQ-классов ===== +
- +
-====  Статический DataContext ====  +
-Доступен как **LinqHelper.Context**. Рекомендуется к использованию вместо создания каждый раз нового контекста через ''​using (QPDataContext ctx = new QPDataContext())'',​ кроме тех случав,​ когда необходимо работать с несколькими контекстами сразу. +
- +
- +
- +
-====  ​Поддержка фильтрации M2M-полей на уровне SQL ====  +
- +
-Для этого генерируются отдельные узловые классы для каждой связи. ​ На уровне БД эти классы отображаются на специальные представления (//​LINK_NNN//​),​ которые ссылаются на единую физическую таблицу //​ITEM_TO_ITEM//​. Все используемые узловые сущности генерируются автоматически,​ если же необходимо заменить их названия,​ это можно сделать в настройках поля Many-To-Many или через тег ''​link''​ [[developer:​linq:​mapping|файла отображения]],​ задав в качестве идентификатора атрибут ID, и желаемые имена – в атрибутах //​mapped_name//​ и //​mapped_plural_name//​.  +
- +
-При написании LINQ-запроса с фильтрацией по M2M-полю на уровне SQL алгоритм следующий:​  +
-    * начинаем с узловой сущности +
-    * фильтруем по одной связанной сущности  +
-    * делаем проекцию по второй связанной сущности +
-<code c#>​IQueryable<​Article>​ art = ctx.ArticlesCategories.Where(n => n.Category.Title == "​main"​).Select(m => m.Article);</​code>​ +
- +
-====  Поддержка механизмом отслеживания изменений полей M2M. ====   +
-Для изменения полей M2M достаточно только вызывать методы //Add// и //Remove// соответствующей коллекции. Никакие другие поля для срабатывания механизма отслеживания изменений изменять не надо.  +
- +
-====  Автоматическая замена плейсхолдеров ====  +
-Поддержка стандартного поведения Publishing Container ([[edit:​url_auto_replace|подробнее]]) Замена плейсхолдеров ''<​%=site_url%>''​ и ''<​%=upload_url%>''​ производится для полей типа //String//, //​VisualEdit//​ и //​Textbox//​. Замена работает в обе стороны,​ как при загрузке,​ так и при сохранении. Для включения этого механизма необходимо у тега ''​schema''​ в [[developer:​linq:​mapping|файле отображения]] выставить атрибут ''​replaceUrls="​true"''​. ​ Кроме того установкой атрибута ''​useLongUrls="​true"''​ можно модифицировать поведение механизма,​ замены так, что плейсхолдеры будут заменяться на абсолютные URL, а не на относительные. +
- +
-==== Фильтрация по умолчанию ==== +
-Поддержка стандартного поведения Publishing Container. Необходимо для того, чтобы работало расписание публицкации,​ workflow, архив. В предыдущих версиях реализовалось через вызов метода расширения **ForFrontEnd()**. В текущей версии метод оставлен для обратной совместимости,​ но в случае включенной фильтрации по умолчанию никаких действий не выполняет. Опция **Использовать фильтрацию по умолчанию** (**Use default filtration**) настраивается [[:​content|на уровне контента]]. При включенной опции LINQ-класс отображается не на таблицу CONTENT_NNN,​ а на представление CONTENT_NNN_LIVE (для stage-режима используется аналогичное представление CONTENT_NNN_STAGE вместо CONTENT_NNN_UNITED) +
-  +
- +
-==== Улучшенная логика служебных полей ==== +
-    * Альтернативные свойства для служебных полей в соответствии с Pascal-нотацией:​ //​Created//,​ //​Modified//,​ //​LastModifiedBy//,​ //Visible// (bool), //Archive// (bool), //​StatusType//​ (ссылка на соответствующий тип). Старые названия поддерживаются для обратной совместимости. +
-    * Автообновление полей //Created// и //​Modified//​ при соответствующих событиях (//​SubmitChanges//​) +
-    * Автосоздание служебных полей (//​Visible//,​ //​Archive//,​ //​StatusType//​) при создании нового экземпляра класса и возможность их изменения. +
- +
-===== Пример использования для ​добавления статьи =====+
 <code c#> <code c#>
  NewsArticle art = new NewsArticle();​  NewsArticle art = new NewsArticle();​
Line 120: Line 72:
  LinqHelper.Context.SubmitChanges();​  LinqHelper.Context.SubmitChanges();​
 </​code>​ </​code>​
- 
-===== LINQ-классы и кэширование ===== 
- 
-В //​quantumart.dll//​ добавлен обобщенный (generic) метод кэширования //​GetCachedEntity//:​ 
- 
-<code c#> 
-public T GetCachedEntity<​T>​(string key, Func<​T>​ fillAction) 
-public T GetCachedEntity<​T>​(string key, Func<​string,​ T> fillAction) 
-public T GetCachedEntity<​T>​(string key, double cacheInterval,​ Func<​T>​ fillAction) 
-public T GetCachedEntity<​T>​(string key, double cacheInterval,​ Func<​string,​ T> fillAction) 
-</​code>​ 
-Основное назначение:​ кэширование произвольных пользовательских коллекций,​ в том числе полученных с помощью LINQ. Обычно это: //​Dictionary<​K,​ V>//, //​List<​T>//​. 
- 
-Пример использования:​ 
- 
-<code c#> 
-DBConnector cnn = new DBConnector();​ 
-List<​Banner>​ banners = cnn.CacheManager.GetCachedEntity<​List<​Banner>>​(pageAddress,​ 10, GetPageBanners);​ 
-</​code>​ 
- 
-Где //​GetPageBanners//​ – метод, возвращающий //​List<​Banner>//,​ который будет вызван только при заполнении кэша. Метод может быть определен как без параметров,​ так и с единственным строковым параметром. Во втором случае в метод будет передано значение ключа (параметр //key//). 
- 
-Метод может использоваться для кэширования любых ссылочных типов. Для того, чтобы кэшировать значимый тип (value type), необоходимо выполнить принудительный //boxing// (число можно для этого, например,​ преобразовать в строку). Данный метод также позволяет кэшировать //​null//​-значения,​ используя для них специальную обертку. 
- 
-===== Основные ошибки использования LINQ-классов ===== 
- 
-  * Многократное выполнение одних и тех же запросов без необходимости. 
-  * Отсутствие понимания,​ что выполняется на веб-сервере,​ а что -- на сервере баз данных  ​ 
-  * Получение от SQL-сервера лишней информации (аналог в SQL -- //select *//), когда можно обойтись ограниченным набором полей с помощью //select new//. 
-  * В конце цепочки операций LINQ в большинстве случаев желательно явно овеществлять результат (например,​ с помощью //​ToList()//​). Результат должен быть не //​IQueryable<​T>//,​ а //​IEnumerable<​T>//​ или T, иначе последующий //foreach// может привести к новому SQL-запросу на каждой итерации. 
  
  
developer/linq.1304585002.txt.gz · Last modified: 2011/05/05 11:43 by celutp