В библиотеку добавлен 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");
};
}
#>
Добавить поддержку остальных провайдеров не проблема, но нужно разбираться с тем как читать из низ метаданные. Если у кого-то есть подобная информация, то добавить другие провайдеры будет проще простого.