T4 шаблон для MS SQL и Sybase
От: IT Россия linq2db.com
Дата: 14.06.10 02:30
Оценка: 96 (11) :)
В библиотеку добавлен T4 шаблон для генерации модели данных по базе данных. На данный момент сделана поддержка для Sybase и MS SQL.

Использовать следующим образом:

1. Копируем файлы шаблона из папки Source\Templates в свой проект.
2. Добавляем в проект новый файл с расширением tt.
3. Редактируем файл следующим образом:

<#@ template language="C#v3.5"         #>
<#@ output extension=".generated.cs"   #>
<#@ include file="BLToolkit.ttinclude" #>
<#@ include file="Sybase.ttinclude"    #>
<#
    ConnectionString     = "Data Source=DBHost;Port=5000;Database=BLToolkitData;Uid=sa";
    DataProviderAssembly = @"......\Sybase.AdoNet2.AseClient.dll";

    Namespace       = "Templates";
    DataContextName = "DataModel";

    GenerateModel();
#>

С ConnectionString, думаю, всё ясно. В DataProviderAssembly указывается путь к сборке дата провайдера. Если сборка установлена в GAC, то вместо этого можно попробовать воспользоваться следующей директивой:

<#@ assembly name="Sybase.AdoNet2.AseClient" #>

Для MS SQL ничего этого делать не нужно. Т.е. шаблон будет выглядеть следующим образом:

<#@ template language="C#v3.5"         #>
<#@ output extension=".generated.cs"   #>
<#@ include file="BLToolkit.ttinclude" #>
<#@ include file="MSSQL.ttinclude"     #>
<#
    ConnectionString = "Server=.;Database=BLToolkitData;Integrated Security=SSPI";

    Namespace        = "Templates";
    DataContextName  = "DataModel";

    GenerateModel();
#>

В Namespace и DataContextName указываются соответсвенно namespace и имя класса, который будет выступать в качестве DataContext. К сожалению, из шаблонов нельзя просто доступиться к проекту студии и эти параметры нужно задавать вручную. В качестве альтернативы можно установить T4 Toolbox и изменить проект слудующим образом:

<#@ template language="C#v3.5" hostspecific="True" #>
<#@ output extension=".generated.cs"     #>
<#@ include file="BLToolkit.ttinclude"   #>
<#@ include file="BLT4Toolkit.ttinclude" #>
<#@ include file="MSSQL.ttinclude"       #>
<#
    ConnectionString = "Server=.;Database=BLToolkitData;Integrated Security=SSPI";

    GenerateModel();
#>

Теперь эти параметры будут браться из проекта, но на компьютере должен быть установлен T4 Toolbox и работать шаблон будет только из под студии.

Генерация кода для VB делается следующим образом:

<#@ template language="C#v3.5"         #>
<#@ output extension=".generated.vb"   #>
<#@ include file="BLToolkit.ttinclude" #>
<#@ include file="MSSQL.ttinclude"     #>
<#@ include file="VB.ttinclude"        #>
<#
    ConnectionString = "Server=.;Database=BLToolkitData;Integrated Security=SSPI";

    Namespace        = "Templates";
    DataContextName  = "DataModel";

    GenerateModel();
#>

Кроме вышеприведённых настроечных переменных проект поддерживает следующие переменные:

string DataContextName;
string Namespace                = "DataModel";
string BaseDataContextClass     = "DbManager";
string BaseEntityClass          = null;
string OneToManyAssociationType = "IEnumerable<{0}>";
bool   RenderField              = false;

Работа шаблона состоит из двух этапов: чтение метаданных из БД и собственно рендеринг. После чтения метаданных их можно подредактировать по своему усмотрению. Следующий пример демонстрирует некоторые возможности:

<#@ template language="C#v3.5"         #>
<#@ output extension=".generated.cs"   #>
<#@ include file="BLToolkit.ttinclude" #>
<#@ include file="MSSQL.ttinclude"     #>
<#
    ConnectionString = "Server=.;Database=BLToolkitData;Integrated Security=SSPI";

    Namespace        = "Templates";
    DataContextName  = "DataModel";

    // Важно! Этот метод нужно вызвать до начала работы с метаданными.
    //
    LoadMetadata();

    // Меняем имя поля BinaryDataID таблицы BinaryData на ID.
    //
    Tables["BinaryData"].Columns["BinaryDataID"].MemberName = "ID";

    // Меняем имя ассоциации FK_Employees_Employees таблицы Employees на ReportsToEmployee.
    //
    Tables["Employees"].ForeignKeys["FK_Employees_Employees"].MemberName = "ReportsToEmployee";

    // Заменям имя поля на "ID", если это поле является первичным ключом и имеет имя ClassNameID.
    // 
    foreach (var t in Tables.Values)
        foreach (var c in t.Columns.Values)
            if (c.IsPrimaryKey && t.TableName + "ID" == c.ColumnName)
                c.MemberName = "ID";

    // Добавляем наследование entity классам от EntityBase<T>.
    //
    Usings.Add("BLToolkit.Common");

    foreach (var t in Tables.Values)
        t.BaseClassName = "EntityBase<" + t.ClassName + ">";

    GenerateModel();
#>

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

Структура метаданных шаблона выглядит следующим образом:

Dictionary<string,Table> Tables = new Dictionary<string,Table>();

partial class Table
{
    public string       Owner;
    public string       TableName;
    public string       ClassName;
    public string       BaseClassName;
    public bool         IsView;
    public List<string> Attributes = new List<string>();

    public Dictionary<string,Column>     Columns     = new Dictionary<string,Column>();
    public Dictionary<string,ForeignKey> ForeignKeys = new Dictionary<string,ForeignKey>();
}

partial class Column
{
    public int          ID;
    public string       ColumnName;
    public string       MemberName;
    public bool         IsNullable;
    public bool         IsIdentity;
    public string       Type;
    public bool         IsClass;
    public DbType       DbType;
    public SqlDbType    SqlDbType;
    public int          PKIndex = -1;
    public List<string> Attributes = new List<string>();

    public bool IsPrimaryKey { get { return PKIndex >= 0; } }
}

enum AssociationType
{
    Auto,
    OneToOne,
    OneToMany,
    ManyToOne,
}

partial class ForeignKey
{
    public string          KeyName;
    public string          MemberName;
    public Table           OtherTable;
    public List<Column>    ThisColumns  = new List<Column>();
    public List<Column>    OtherColumns = new List<Column>();
    public ForeignKey      BackReference;
    public AssociationType AssociationType;
}

При желании, манипуляции с метаданными можно оформить в виде отдельного файла как это сделано для атрибутов WCF:

<#@ template language="C#v3.5"             #>
<#@ output extension=".generated.cs"       #>
<#@ include file="BLToolkit.ttinclude"     #>
<#@ include file="MSSQL.ttinclude"         #>
<#@ include file="WCFAttributes.ttinclude" #>
<#
    ConnectionString = "Server=.;Database=BLToolkitData;Integrated Security=SSPI";

    Namespace        = "Templates";
    DataContextName  = "DataModel";

    GenerateModel();
#>

Этот шаблон добавляет атрибуты DataContract классам и DataMember свойствам модели и выглядит следующим образом:

<#
    {
        var wcfPrevBeforeGenerateModel = BeforeGenerateModel;
    
        BeforeGenerateModel = tt =>
        {
            wcfPrevBeforeGenerateModel(tt);

            Usings.Add("System.Runtime.Serialization");

            foreach (var t in Tables.Values)
                t.Attributes.AddRange(new[] { "Serializable", "DataContract" });

            foreach (var t in Tables.Values)
                foreach (var c in t.Columns.Values)
                    c.Attributes.Add("DataMember");
        };
    }
#>

Добавить поддержку остальных провайдеров не проблема, но нужно разбираться с тем как читать из низ метаданные. Если у кого-то есть подобная информация, то добавить другие провайдеры будет проще простого.
Если нам не помогут, то мы тоже никого не пощадим.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.