Feel Good.

14 апреля 2010

Используем процессор шаблонов. NVelocity.

В этой статье я расскажу о том, как можно решить проблему создания представлений для моделей, используя NVelocity. Классическая задача программиста: имея бизнес объект(Model) необходимо построить для него представление(View). При решении подобной задачи, требуется помнить о том, что для конкретной модели может быть несколько представлений, в то время, как модель даже о них и не догадывается. На сегодняшний день существует несколько известных View Engine:
  • System.Web.Mvc.WebFormViewEngine
  • Brail
  • NDjango
  • NHaml
  • NVelocity
  • Spark View Engine

Использование каждого, это дело вкуса (сравнение), но в этой статье я хотел бы поделиться опытом использования мною NVelocity в своем проекте. Скачать NVelocity можно как отдельно в виде библиотеки, так и в составе СastleProject. Последнее идет полной сборкой и добавляет в Visual Studio поддержку intellisense. После того как вы установили NVelocity, необходимо добавить в проекте reference, это сделает доступным следующие пространства имен:

using NVelocity;

using NVelocity.App;

using NVelocity.Context;

Подготовительная часть пройдена. Вернемся к нашему проекту и озвучим постановку задачи: дан источник данных, возвращающий коллекцию элементов типа Person, оформленный в виде статического метода:

static Person[] GetFriends()

{

    Person[] group =

    {

        new Person { Name = "Илья",    Age = 24, IsMale = true },

        new Person { Name = "Алексей", Age = 22, IsMale = true },

        new Person { Name = "Наталья", Age = 25, IsMale = false },

        new Person { Name = "Борис", Age = 23, IsMale = true }

    };

    return group;

}


Где тип Person описывается следующим классом:

class Person

{

    // Имя

    public String Name { get; set; }

    // Возраст

    public int Age { get; set; }

    // Пол

    public bool IsMale { get; set; }

}


Для данной модели(коллекция, возвращаемая методом GetFriends()) необходимо создать представление в виде следующего XML файла:

<?xml version="1.0" encoding="utf-8"?>

 

<group name="Друзья">

    <person

        name="ИЛЬЯ"

        age="24"

        gender="м" />

    <person

        name="АЛЕКСЕЙ"

        age="22"

        gender="м" />

    <person

        name="НАТАЛЬЯ"

        age="25"

        gender="ж" />

    <person

        name="БОРИС"

        age="23"

        gender="м" />

</group>


Приступаем выполнению, и первое что делаем, это пишем шаблон(Template). Шаблон реализуется на интуитивно понятном языке Velocity Template Language (VTL). Для этого добавим в проект файл xmlTemplate.vm со следующим содержимым:

## Инициализация локальных переменных

#set($male = "м")

#set($female = "ж")

## Шаблон

<?xml version="1.0" encoding="utf-8"?>

 

<group name="$groupName">

#foreach( $person in $group)

    <person    

        name="$person.Name.ToUpper()"

        age="$person.Age"

        gender="#if($person.IsMale)$male#else$female#end" />

#end

</group>


Самое главное мы реализовали, осталось лишь выполнить инициализацию нашего View Engine, указать ему шаблон, подсунуть модель и получить на выходе готовое представление:

static void Main(string[] args)

{

    // Получим модель

    Person[] group = GetFriends();

 

    // Инициализируем ViewEngine.             

    Velocity.Init();

 

    // Инициализируем контекст моделью.

    VelocityContext context = new VelocityContext();

    context.Put("groupName", "Друзья");

    context.Put("group", group);

 

    using (StringWriter writer = new StringWriter())

    {

        // Используя шаблон, построим отображение модели.

        Velocity.MergeTemplate

            (

            @"xmlTemplate.vm",

            Encoding.UTF8.WebName,

            context,

            writer

            );

 

        Console.WriteLine(writer.GetStringBuilder().ToString());

    }

    Console.ReadKey();

}


Преимущества данного подхода заключается в том, что мы можем изменять шаблон (xmlTemplate.vm) и при этом не трогая(!) исходный код. Во-вторых, исходный код программы при таком подходе лишен громоздких, формирующих представление модели, конструкций. В-третьих, получившийся шаблон не зависит ни от платформы, ни от языка на котором реализован сам движок.


Progg it

10 комментариев:

  1. Не вижу никакого смысла использовать в данном случае "View Engine"-движков, имхо. Можно обойтись простой xml-сериализацией. Пример не демонстрирует выигрыша от использования View Engine, но все равно спс за статью

    ОтветитьУдалить
  2. @Wanderer

    Спасибо за комментарий.
    На данном примере да. Но все же, если мы захотим получить не xml, а например представление в json (задача на будущее), то здесь нам не обойтись без View Engine.
    Да и поправить шаблон гораздо удобнее и нагляднее, нежели перестраивать сериализацию.

    ОтветитьУдалить
  3. ну почему же, можно воспользоваться стандартными средствами для сериализвации в json (класс System.Runtime.Serialization.Json.DataContractJsonSerializer). Хотя это все тот же частный случай, как и с xml.. Я думаю не стоит говорить о том, что лучше использовать в данном случае или сериализацию (xml или json) или View Engine - дело вкуса, как и в том так и в другом случае будут накладные расходы: в первом случае, если понадобиться изменить сериализуемое представление, во-втором - изучение определенного View Engine движка.
    Хотелось именно посмотреть на случаи, где View Engine действительно был бы необходим.

    ОтветитьУдалить
  4. @Wanderer

    Для более сложных шаблонов, например получить HTML, RTF или CSV, YAML или XSL-FO.
    Здесь главное использование подхода Model-View (шаблон).

    ОтветитьУдалить
  5. IsMale = true

    попахивает дискриминацией

    ОтветитьУдалить
  6. @hazzik

    Да не, Вам показалось :).
    А если серьезно, то конечно нужно было бы завести enum.

    ОтветитьУдалить
  7. Мне тоже нравится NVelocity. Если говорить о реальных примерах применения, то часто template engines используются для генерации электронных писем. Особенно когда нужно генерировать сложное письмо с циклами и условиями. Но, к сожалению, реализация Velocity под .NET не лишена недостатков. Сами castle признаются, что, например, макросы работают не всегда корректно.

    ОтветитьУдалить
  8. @admax
    Согласен, не все у них гладко. Просто сам язык Velocity меня привлекает. Да и много других шаблонизаторов основаны на Velocity.

    ОтветитьУдалить
  9. Я так понимаю, NVelocity можно использовать для кодогенерации. Или я ошибаюсь?

    ОтветитьУдалить
  10. @Barsym
    Да, можно.
    Но лучше код генерировать этим: T4 Templates in Visual Studio for Code Generation

    ОтветитьУдалить