User Tools

Site Tools


developer:linq

This is an old revision of the document!


LINQ-to-SQL и контенты QP7.Framework

Решаемые задачи

Работа с контентами упрощается за счет того, что:

  • Доступ к полям становится типизированным, с поддержкой IntelliSense.
  • LINQ-to-SQL предоставляет большое количество встроенных операций для работы с наборами данных. Единицами данных в этих наборах в нашем случае будут являться статьи.
  • Становится доступным поддержка декларативного связывания контентов с контролами (по DataSourceID) с помощью стандартного класса LinqDataSource.
  • Добавляется возможность редактирования статей с помощью LINQ-классов. Что вместе с предыдущим пунктом дает возможность использования, например, редактируемого Grid для контентов QP7.
  • Связанные таблицы становятся доступными как свойства (включая связи многие-ко-многим).

Предварительная настройка

  • В конфигурационный файл Backend нужно добавить параметр SqlMetalPath, определяющий путь к стандартной утилите генерации SQL-классов. Данная утилита устанавливатся вместе с Visual Studio 2008. Путь к ней может выглядеть так: C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\sqlmetal.exe. Если на целевом компьютере не установлена Visual Studio, утилиту можно просто скачать.
  • Необходимо дать право доступа Modify пользователю NETWORK SERVICE на папку App_Code для возможности генерации файлов классов и папку App_Data для генерации промежуточных файлов отображений.
  • В процессе работы утилита SQLMetal может выдавать ошибки, которые можно посмотреть в файле sqlmetal.log в папке App_Data. Ошибки обычно связаны с конфликтами имен.
  • Если на сайте не использовался LINQ-to-SQL, то необходимо добавить в web.config ссылки на соответствующие сборки:
<system.web>
	<compilation>
		<assemblies>
...
			<add assembly="System.Data.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
			<add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
			<add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
		</assemblies>
...
	</compilation>
</system.web>
...
  <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cs" warningLevel="4" type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <providerOption name="CompilerVersion" value="v3.5" />
        <providerOption name="WarnAsError" value="false" />
      </compiler>
    </compilers>
  </system.codedom>

Настройка сборки контентов в классы LINQ-to-SQL

Сборка контентов QP7 в классы LINQ-to-SQL может осуществляться в двух режимах:

  1. С использованием БД. При этом будут использованы специальные настройки LINQ-to-SQL, которые задаются в свойствах контентов и полей.
  2. С использованием пользовательского файла отображения. При этом файл может быть как составлен разработчиком вручную, так и сгенерирован бэкендом по умолчанию.

Переключение режимов осуществляется опцией Использовать прямое отображение из базы данных (Use direct mapping from database) на уровне сайта. Cама сборка запускается кнопкой Собрать Контенты (Assemble Contents) на странице Контенты (Contents). При этом в папке App_Code происходит полная перегенерация всех LINQ-классов текущего сайта. Следует иметь в виду, что данный процесс с большой долей вероятности приведет к перегрузке домена приложения, поэтому его стоит с осторожностью применять на продукционных сайтах.

Кроме этого, на уровне сайта существует опция Импортировать файл отображения в базу данных(Import mapping file to database), которая позволяет импортировать существующий пользовательский файл отображения в базу данных для последующей настройки через бэкенд QP7 и генерации файла отображения в автоматическом режиме. При успешном выполнении импорта данная опция автоматически сбрасывается, а опция Использовать прямое отображение из базы данных (Use direct mapping from database) автоматически устанавливается.

Опция Импортировать файл отображения в базу данных(Import mapping file to database) также может быть полезна для первичного заполнения настроек LINQ-to-SQL для существующего сайта с большим количеством контентов. Для этого сначала генерируется пользовательский файл отображения по умолчанию (при отключенной опции Использовать прямое отображение из базы данных (Use direct mapping from database), а затем проводится его импорт.

Настройка отображения с использованием БД

Настройки уровня сайта

  • Имя строки подключения (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, а не на относительные (по умолчанию). Аналогичная опция есть для Publishing Container'ов.
  • Пространство имен для генерируемых классов (Namespace for generated classes) – Пространство имен, в котором будут сгенерированы LINQ-классы. По умолчанию - пустое.
  • Имя контекстного класса (Context Class Name) – Имя контекстного класса. По умолчанию - QPDataContext.
  • Выполнять генерацию, независимую от БД (Proceed DB-independent generation) – Включение данной опции позволяет генерировать код, переносимый между различными БД. В этом случае сопоставление между сущностными классами и таблицами БД, осуществляется с помощью специального .map-файла, который также генерируется в этом режиме. Имя .map-файла совпадает с именем контекстного класса.
  • Генерировать только .map-файл (Generate only .map file) – Модификация предыдущей опции. Нужна в случае выполнения на данной БД переносимого кода, который был сгенерирован на другой БД. Таким образом, от данной БД требуется только сопоставление между сущностными классами и таблицами БД.

Настройки уровня контента

  • Отображать как класс (Map as class) – Генерация класса для данного контента производится только при включенной опции.
  • Имя (единственное) (Name (singular)) – Допустимое в С# имя контента в единственном числе. Используется как название класса.
  • Имя (множественное) (Name (plural)) – Допустимое в С# имя контента во множественном числе. Используется как название свойства контекстного класса, с которого начинается построение LINQ-to-SQL запроса.
  • Использовать фильтрацию по умолчанию (Use default filtration) – Включение данной опции необходимо для реализации стандартного поведения Publishing Container (поддержка расписания публикации, workflow, архива). В предыдущих версиях реализовалось через вызов метода расширения ForFrontEnd(). В текущей версии метод оставлен для обратной совместимости, но в случае включенной фильтрации по умолчанию никаких действий не выполняет. При включенной опции LINQ-класс отображается не на таблицу CONTENT_NNN, а на представление CONTENT_NNN_LIVE (для stage-режима используется аналогичное представление CONTENT_NNN_STAGE вместо CONTENT_NNN_UNITED.

Настройки уровня поля

  • Отображать как LINQ-свойство (Map as LINQ Property) – Генерация свойства для данного поля производится только при включенной опции.
  • Имя 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 файла отображения, задав в качестве идентификатора атрибут ID, и желаемые имена – в атрибутах mapped_name и mapped_plural_name.

При написании LINQ-запроса с фильтрацией по M2M-полю на уровне SQL алгоритм следующий:

  • начинаем с узловой сущности
  • фильтруем по одной связанной сущности
  • делаем проекцию по второй связанной сущности
IQueryable<Article> art = ctx.ArticlesCategories.Where(n => n.Category.Title == "main").Select(m => m.Article);

Поддержка механизмом отслеживания изменений полей M2M.

Для изменения полей M2M достаточно только вызывать методы Add и Remove соответствующей коллекции. Никакие другие поля для срабатывания механизма отслеживания изменений изменять не надо.

Автоматическая замена плейсхолдеров

Поддержка стандартного поведения Publishing Container (подробнее) Замена плейсхолдеров <%=site_url%> и <%=upload_url%> производится для полей типа String, VisualEdit и Textbox. Замена работает в обе стороны, как при загрузке, так и при сохранении. Для включения этого механизма необходимо у тега schema в файле отображения выставить атрибут replaceUrls=“true”. Кроме того установкой атрибута useLongUrls=“true” можно модифицировать поведение механизма, замены так, что плейсхолдеры будут заменяться на абсолютные URL, а не на относительные.

Фильтрация по умолчанию

Поддержка стандартного поведения Publishing Container. Необходимо для того, чтобы работало расписание публицкации, workflow, архив. В предыдущих версиях реализовалось через вызов метода расширения ForFrontEnd(). В текущей версии метод оставлен для обратной совместимости, но в случае включенной фильтрации по умолчанию никаких действий не выполняет. Опция Использовать фильтрацию по умолчанию (Use default filtration) настраивается на уровне контента. При включенной опции 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) при создании нового экземпляра класса и возможность их изменения.

Пример использования для добавления статьи

	NewsArticle art = new NewsArticle();
        art.Title = "some title";
	art.Text = "some text";
	LinqHelper.Context.NewsArticles.InsertOnSubmit(art);
	LinqHelper.Context.SubmitChanges();

LINQ-классы и кэширование

В quantumart.dll добавлен обобщенный (generic) метод кэширования GetCachedEntity:

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)

Основное назначение: кэширование произвольных пользовательских коллекций, в том числе полученных с помощью LINQ. Обычно это: Dictionary<K, V>, List<T>.

Пример использования:

DBConnector cnn = new DBConnector();
List<Banner> banners = cnn.CacheManager.GetCachedEntity<List<Banner>>(pageAddress, 10, GetPageBanners);

Где GetPageBanners – метод, возвращающий List<Banner>, который будет вызван только при заполнении кэша. Метод может быть определен как без параметров, так и с единственным строковым параметром. Во втором случае в метод будет передано значение ключа (параметр key).

Метод может использоваться для кэширования любых ссылочных типов. Для того, чтобы кэшировать значимый тип (value type), необоходимо выполнить принудительный boxing (число можно для этого, например, преобразовать в строку). Данный метод также позволяет кэшировать null-значения, используя для них специальную обертку.

Основные ошибки использования LINQ-классов

  • Многократное выполнение одних и тех же запросов без необходимости.
  • Отсутствие понимания, что выполняется на веб-сервере, а что – на сервере баз данных
  • Получение от SQL-сервера лишней информации (аналог в SQL – select *), когда можно обойтись ограниченным набором полей с помощью select new.
  • В конце цепочки операций LINQ в большинстве случаев желательно явно овеществлять результат (например, с помощью ToList()). Результат должен быть не IQueryable<T>, а IEnumerable<T> или T, иначе последующий foreach может привести к новому SQL-запросу на каждой итерации.

Discussion

Enter your comment
 
developer/linq.1304585002.txt.gz · Last modified: 2011/05/05 11:43 by celutp