• Microsoft .NET
  • LINQ
  • LINQtoSQL: Модификация в соответствии с требованиями код, генерируемый конструктором

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, пока проектировщик хорошо выполняет требуемую вам работу.