В прошлый раз мы рассмотрели возможность управления внешним видом скаффолд-приложений с помощью аннотирования мета-классов. Очевидно, что создание таких мета-классов — рутинная задача. Попробуем упростить себе жизнь с помощью кодогенерации.
Предлагаемый метод не даёт на выходе 100% готовый к использованию код. Скорее, это помощник для генерации заготовки кода, которую далее всё равно придётся менять. Конечно, можно было бы написать плагин с UI для Visual Studio, но это заняло бы гораздо больше времени. :)
Мы будем использовать встроенный в студию движок T4 для генерации заготовки кода. Никаких дополнительных плагинов не нужно.
Хочется порекомендовать отличный блог Олега Сыча (en), в котором он подробнейшим образом описывает возможности этого движка, делится секретами и лучшими практиками. Для быстрого ознакомления с T4, можно прочесть статью Александра Полозова на хабре.
Итак, что нам нужно получить от кодогенератора? Linq to SQL создал для нас класс контекста, а также объектную модель данных (набор partial классов). Для каждого класса этой модели мы должны создать заготовку мета-класса и навесить на её свойства базовые атрибуты. Таким образом, то, что мы получили в предыдущем посте, будет производиться автоматически для всех классов модели. Нам нужно будет потом только править результат.
Начнём с того, что создадим новый файл в проекте с расширением *.tt. Я назвал его MetaClassesGenerator.tt. Мы должны указать путь к сборке, содержащей сгенерированные LINQ To SQL классы, а также указать пространство имён, в котором их надо искать.
Код T4 шаблона:
<#@ template language="C#v3.5" debug="true" #>
<# // Результирующий файл будет в формате txt, сгруппирован с tt файлом #>
<#@ output extension="txt" #>
<#@ assembly name="System.Core.dll" #>
<#@ assembly name="System.Data.Linq.dll" #>
<# // Добавьте путь к своей dll #>
<#@ assembly name="...\My Documents\Visual Studio 2008\Projects\DynamicDataMvcScaffolding\DynamicDataMvcScaffolding\bin\DynamicDataMvcScaffolding.dll" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#
string ModelsNamespace = "DynamicDataMvcScaffolding.Models";
try
{
// Подставьте тип своего контекста
Type someTypeInNamespace = typeof(DynamicDataMvcScaffolding.Models.NorthwindDataContext);
var asm = System.Reflection.Assembly.GetAssembly(someTypeInNamespace);
List<Type> TableTypes = new List<Type>();
foreach (Type type in asm.GetTypes())
{
if (type.Namespace == ModelsNamespace)
{
Attribute[] attrs = System.Attribute.GetCustomAttributes(type);
if ((from a in attrs
where a.GetType().Name == "TableAttribute"
select type.Name).ToList().Count > 0)
{
TableTypes.Add(type);
}
}
}
#>
/***
This code was generated by MetaClassesGenerator tool
http://aspnetmvcfan.blogspot.com/
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.DynamicData;
namespace <#= ModelsNamespace #>
{
<#
foreach (Type type in TableTypes)
{
#>
#region Meta data for <#= type.Name #>
[ScaffoldTable(true)]
[MetadataType(typeof(<#= type.Name #>MetaModel))]
public partial class <#= type.Name #>
{
}
[DisplayName("<#= type.Name #>")]
public class <#= type.Name #>MetaModel
{
<#
var props = (from p in type.GetProperties()
from a in System.Attribute.GetCustomAttributes(p)
where a.GetType().Name == "ColumnAttribute" || a.GetType().Name == "AssociationAttribute"
select p.Name).ToList();
foreach (var prop in props)
{
PushIndent("\t\t");
WriteLine(String.Format("[DisplayName(\"{0}\")]", prop));
WriteLine("[ScaffoldColumn(true)]");
WriteLine(String.Format("public object {0} {{get; set;}}", prop));
WriteLine("");
PopIndent();
}
#>
}
#endregion
<#
}
#>
}
<#
}
catch (Exception e)
{
WriteLine("// "+ e.Message);
}
#>
Код во многом похож на шаблон ASP, не так ли? В конечном итоге, после сохранения tt файла, будет произведена автоматическая компиляция и под MetaClassesGenerator.tt появится файл MetaClassesGenerator.txt. Всё, что нам надо сделать — скопировать всё, что внутри и вставить в новый cs файл, в котором и будем в дальнейшем настраивать нашу мета-модель..
No comments:
Post a Comment