LINQtoSQL: Модификация в соответствии с требованиями код, генерируемый конструктором
ОГЛАВЛЕНИЕ
Введение
LINQ to SQL используется для работы с данными на сервере баз данных SQL. Visual Studio поставляется с проектировщиком LINQtoSQL, который является мощным инструментом для генерирования LINQ. Он быстрый, простой в использовании, и имеет много других положительных сторон.
Хотя есть и недостаток. Вы не можете изменить сгенерированный код. Как и все другие проектировщики, он работает, в нем легко запустить демонстрационную программу, но когда вы оказываетесь в реальных жизненных ситуациях, вы всегда хотите немного большего. Это большее всегда связано с какой-то проблемой. Поэтому мы не будем пытаться создать лучший код LINQ to SQL. Мы просто покажем вам, как изменить и обогатить сгенерированный код за считанные минуты. Изменение в соответствии с требованиями пользователя полностью прозрачно для стандартного проектировщика LINQtoSQL, и вы по-прежнему сможете использовать его для моделирования классов и доступа к данным.
Краткие пояснения
Можно разбить задачу на три сценария, зависящие от того, насколько отличается желаемый сгенерированный код от того, который генерирует LINQtoSQL. Каждый сценарий будет подробно обсуждаться ниже, и будет реализован на основе бесплатного средства Visual Studio, называемого Reegenerator.
С помощью этого средства вы определяете в решении отдельный проект, который содержит сгенерированный код, не являющийся частью текущего варианта программы. Затем вы присоединяете элементы проекта LINQtoSQL (.dbml) к классам сгенерированного кода, определенным в этом проекте. Когда вы используете проектировщик LINQtoSQL и сохраняете файл, генерация кода будет вызвана автоматически, чтобы создать .Designer.cs, который зависим от файла DBML.
Использование кода
Загрузите файл архива по адресу. Вам понадобятся установленные Visual Studio 2008 и Reegenerator. Извлеките файлы из архива и загрузите файл решения в Visual Studio. Решение содержит проект, названный CustomizeDesigners, который содержит генераторы кода, и проект под названием Business, содержащий файлы DBML и базу данных, которая используется в качестве источника для проектировщика LINQtoSQL. В обоих проектах Business и RenderersLibrary есть три папки Scenario (сценарий), которые содержат DBML и генераторы кода для каждого сценария, как описано ниже.
Чтобы запустить генерацию кода, вам нужно отредактировать и сохранить файл DBML, или щелкнуть на нем правой кнопкой мыши и вызвать "Запуск клиентского инструментария" ("Run Custom Tool"). Для отладки нужно задать точку останова в коде генератора кода в проекте RenderersLibrary и запустить сеанс отладки. Сеанс вызовет второй экземпляр Visual Studio. Загрузите то же самое решение во второй экземпляр и запустите генерацию кода, сохранив файл DBML. Сгенерированный код будет сохранен как элемент проекта (обычно ".Designer.cs"), зависимый от файла DBML. Необходимо щелкнуть мышкой на значке с изображением плюса, расположенном с левой стороны элемента проекта DBML, чтобы раскрыть список его потомков.
Чтобы проверить, к чему присоединен элемент проекта DBML, щелкните на нем правой кнопкой мыши и нажмите на "Attach Renderers (присоединенные рендереры)...".
Рисунок ниже показывает, как будет выглядеть ваша рабочая среда:Сценарий 1 – Небольшие изменения
В этом случае мы, скорее всего, просто хотим вызывать генератор кода по умолчанию LINQtoSQL, захватить получившийся код как строку, управлять ей так, как нам нужно, и затем отправить измененный код обратно в Visual Studio.
Файл DBML, расположенный в папке Сценарий1 в бизнес-проекте, связан с классом CustomizeDesigners\Scenario1\CustSmallChangesToDefaultLINQtoSQL. Код, выполняющий требуемые изменения, довольно простой:
public class SmallChangesToDefaultLINQtoSQL : Generators.CodeRenderer
{
/// <summary>
/// Устанавливаемое по умолчанию средство изменения в соответствии с требованиями //пользователя для файлов dbml.
/// </summary>
public const string CustomToolName = "MSLinqToSQLGenerator";
public override Generators.RenderResults Render()
{
// запустить установленное по умолчанию средство изменения.
byte[] resultsAsBytes = base.RunOtherCustomTool(CustomToolName);
// получить сгенерированный Microsoft код как строку.
string results = System.Text.Encoding.Default.GetString(resultsAsBytes);
// преобразовать строку.
string modifiedResults = AddSomething(results); // AddAttribute(results) //добавить атрибут;
// вернуть строку и сохранить ее в файле .Designer.cs.
return new Generators.RenderResults(modifiedResults);
}
private string AddSomething(string results)
{
results = string.Format(@"// -------------------------------------------------------
// Automatically generated.
// Generation date: {0}
// Generated by: {1}
// -------------------------------------------------------
{2}",
System.DateTime.Now.ToString("yyyy-MM-dd hh:mm"),
System.Security.Principal.WindowsIdentity.GetCurrent().Name,
results);
return results;
}
}
Как можно видеть в вышеприведенном коде, мы вызываем стандартный генератор кода, чтобы извлечь код, сгенерированный проектировщиком Microsoft. Вы можете изменять этот код, добавляя к нему дополнительный код или заменяя определенные фрагменты кода с помощью процедур работы со строками или с помощью регулярных выражений.
Для проверки отредактируйте файл Business\Scenario1\CustomersLINQ.dbml в проекте Бизнес, сохраните его, раскройте список его потомков и откройте файл Business\Scenario1\CustomersLINQ.Designer.cs. Можно увидеть, что файл содержит дополнительный код, добавленный к коду Microsoft.
Для отладки установите точку останова в методе Render, запустите сеанс отладки, загрузите то же самое решение во второй экземпляр Visual Studio и выполните такие же действия, как и при тестировании примера.
Пример генератора кода имеет другой метод, AddAttribute, который показывает, как выполнять немного более сложные действия. Вы можете использовать регулярные выражения, если требуемые изменения более сложные.
Сценарий 2 – Существенные изменения
В этом сценарии вас устраивает сгенерированный Microsoft код, но вам нужен дополнительный код. Примером дополнительного кода может быть добавление метода к каждому классу, сгенерированному из таблицы, или создание сценариев SQL для объектов базы данных.
Самый простой способ сделать это – связать файл DBML с двумя генераторами кода. Первый вызывает генератор кода Microsoft, и вы можете захотеть немного изменить его работу, как в сценарии 1. Второй генерирует дополнительный код.
Чтобы правильно скомпоновать генератор кода для файла DBML, вам нужно иметь возможность десериализовать его в упорядочиваемый экземпляр класса. Это достигается путем включения схемы в проект генератора кода, который создает упорядочиваемые классы с помощью этой схемы. Второй генератор кода десериализует файл DBML в такой класс и затем использует его для генерации требуемого кода.
Файл схемы DBML можно найти в C:\Program Files\Microsoft Visual Studio 9.0\Xml\Schemas\DbmlSchema.xsd,CustomizeDesigners к Dbml\DbmlSchema.xsd. Он связан с генератором кода CustomizeDesigners\XsdRenderers.rgt, который вызывает тот же механизм, что и xsd.exe, чтобы создавать сериализуемые классы с помощью схем. Раскройте список потомков элементов проекта для DbmlSchema.xsd, чтобы увидеть сериализуемые классы. предполагая, что вы установили Visual Studio в каталог по умолчанию. Эта схема была добавлена в проект
Business\Scenario2\CustomersLINQ.dbml связан с двумя генераторами кода (щелкните на нем правой кнопкой мыши и нажмите "Присоединенные рендереры...", чтобы проверить), и два файла .cs будут созданы/обновлены, когда вы сохраните их. Файл Business\Scenario2\CustomersLINQ.Designer.csCustomizeDesigners\Scenario2\SmallChangesToDefaultLINQtoSQL.cs таким же образом, как и для сценария 1. генерируется с помощью
Файл Business\Scenario2\CustomersLINQ.Hello.cs генерируется с помощью CustomizeDesigners\Scenario2\AddHelloMethodToTables.rgt. Это файл шаблона генератора кода, который выглядит почти как страница ASPX. Также есть код, отвечающий за логику работы, хранящийся в файле CustomizeDesigners\Scenario2\AddHelloMethodToTables.rgt.cs. Этот код написан вручную. CustomizeDesigners\Scenario2\AddHelloMethodToTables.Designer.cs содержит код шаблона. Вместе с кодом, отвечающим за логику работы, он формирует класс, который будет вызван, когда файл DBML будет сохраняться.
Шаблон генератора кода CustomizeDesigners\Scenario2\AddHelloMethodToTables.rgt выглядит так:
<%@ Template Language="C#" ClassName="AddHelloMethodToTables" %>
<%@ Import Namespace="System" %>
<%@ Import Namespace="CustomizeDesigners.Dbml" %>
// -------------------------------------------------------
// Автоматически сгенерировано Reegenerator
// Generator: CustomizeDesigners.Scenario2.AddMethodToTables
// Generation date: <%= System.DateTime.Now.ToString("yyyy-MM-dd hh:mm") %>
// Generated by: <%= System.Security.Principal.WindowsIdentity.GetCurrent().Name %>
// -------------------------------------------------------
namespace <%= base.ProjectItem.CodeNamespace %>
{
<% RenderTables(); %>
}
<%@ Method Name="RenderTable" %>
<%@ Parameter Name="table" Type="Table" %>
partial class <%= table.Type.Name %>
{
public string Hello()
{
return "Hello from table <%= table.Type.Name %>";
}
}
<%/ Method %>
В отличие от ASPX, вы можете определить методы визуализации, которые помогут вам разбить работу по генерации кода на много более легко управляемых частей. Заметим, что <% RenderTables(); %> вызывается под пространством имен. Он вызывает функцию, которая написана вручную в части кода, отвечающей за логику работы.
Часть кода, отвечающего за логику работы шаблона Scenario2\AddHelloMethodToTables.rgt.cs, выглядит так:
public partial class AddHelloMethodToTables
{
/// <summary>
/// Элемент базы данных десериализуется из файла dbml.
/// </summary>
Database _database = null;
/// <summary>
/// Выполняется перед визуализацией. Десериализует файл dbml file в свойство _database.
/// </summary>
public override void PreRender()
{
base.PreRender();
// десериализует файл dbml в свойство private.
this._database = DbmlSchemaFactory.Create<Database>(base.ProjectItem);
}
/// <summary>
/// Отображает все таблицы.
/// </summary>
private void RenderTables()
{
if (this._database.Table == null)
return;
foreach (Table table in this._database.Table)
{
RenderTable(table);
}
}
}
Файл DBML десериализуется в свойство _database перед выполнением генерации кода. Учтите, что реализация RenderTables вызывается из файла шаблона. В свою очередь, он вызывает RenderTable(Table table), который определен в шаблоне.
Чтобы проверить это, отредактируйте Business\Scenario2\CustomersLINQ.dbml, сохраните его, раскройте список его потомков и проверьте содержимое двух cs-файлов потомков.
Для отладки установите точку останова в методе PreRender, запустите сеанс отладки, загрузите то же самое решение во второй экземпляр Visual Studio и выполните те же действия, что и при тестировании.
Сценарий 3 – Полное переписывание
Этот сценарий применяется, когда вас полностью не устраивает код, сгенерированный проектировщиком, или вы хотите использовать проектировщик для чего-то другого (например, создать чистые бизнес-объекты, которые не имеют никакого отношения к LINQtoSQL).
CustomizeDesigners\Scenario3\DbmlRenderer.rgt достаточно хорошо заменяет генератор кода Microsoft. По сути, его использование имеет мало смысла, если код Microsoft вас во многом устраивает, ваша задача может относиться к Сценарию 1 или 2.
Его задачей является показать вам пример и познакомить с тем, как можно изменить генератор кода под требования пользователя, используя преимущества уже существующего проектировщика.
Business\Scenario3\CustomersLINQ.dbml связан с CustomizeDesigners\Scenario3\DbmlRenderer.rgt, при этом вы можете выполнить тестирование и отладку таким же образом, что и для предыдущих сценариев.
Последнее замечание
Возможности того, что вы можете сделать с помощью проектировщика LINQtoSQL, неограниченны. Ниже приведено несколько примеров:
- Вы можете создать пользовательские модули для LINQtoSQL, которые магическим образом создаются, когда вы используете проектировщик.
- Вы можете генерировать что-то другое, например сценарии SQL для таблиц и хранимые процедуры, используемые файлом DBML.
- Вы можете использовать проектировщик для задач, абсолютно не связанных с LINQtoSQL, пока проектировщик хорошо выполняет требуемую вам работу.