среда, 11 мая 2011 г.

ASP.NET MVC 3 + Entity Framework 4.1 Code First

Как вы все знаете, недавно вышло обновление MVC 3 Tools, ключевой особенностью которого является более продвинутый scaffolding. Кстати, scaffolding теперь есть и для движка Razor, что тоже плюс. Об этом, а также о недавнем обновлении Entity Framework я сегодня и расскажу. В этой статье речь пойдет про сами обновления, а в следующей будут примеры использования.

Забегая вперед скажу, что scaffolding (по крайней мере, в случае ASP.NET MVC) это возможность быстро генерировать простую логику и интерфейс, которую можно использовать как прототип или в качестве отправной точки в дальнейшей разработке. Также этот подход может работать почти в чистом виде для простого административного интерфейса.

MVC (Model-View-Controller)

Наверняка, 99% читателей знают что такое MVC, однако, на всякий случай, расскажу очень коротко, а подробнее можно посмотреть в википедии (русскоязычной или англоязычной) и приведенных в ней ссылках.

MVC – это шаблон проектирования (хотя часто под ним подразумевают фреймворки, использующие этот шаблон). Основным принципом MVC является разделение ответственности между тремя составляющими:

  • Model – содержит данные и бизнес-логику, в “чистом” MVC, модель ничего не знает о представлениях и контроллерах
  • View – отвечает за представление модели (в основном, в пользовательском интерфейсе), для одной модели может быть несколько представлений
  • Controller – отвечает за поведение в ответ на действия пользователя (управляет реакцией модели и представления)

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

ASP.NET MVC

ASP.NET MVC – это фреймворк от Microsoft. Если рассматривать ASP.NET, то ASP.NET MVC является альтернативой технологии ASP.NET Web Forms.

В далеком 2008 году, когда ASP.NET MVC был еще в стадии CTP, я опубликовал на TechDays доклад о сравнении этих двух технологий, который вызвал нешуточные споры, хотя я и предупреждал о том, что это не более, чем мое мнение :) Тех, кто захочет послушать, сразу хочу предупредить – делал я его глубоко ночью и нереально уставшим, поэтому, для экономии времени, можно просто прочитать презентацию. Также может быть интересно и весело почитать комментарии к моему посту на GotDotNet.

Здесь я повторю два основных преимущества ASP.NET MVC по сравнению с Web Forms “на контролах” (повторюсь, это мое личное мнение):

  • Естественная для web модель “запрос-ответ” (как следствие – если вы хороший web-разработчик, то MVC вам будет ближе, в т.ч. и интеграцией с jQuery).
  • Разделение ответственности (как следствие – лучше тестировать и поддерживать код). Есть конечно и другая сторона медали – эта технология не дает гарантии, что код будет хорошим. Однако, на мой взгляд, написать плохой код на MVC значительно сложнее, чем на “контролах” и code-behind.

Первый релиз ASP.NET MVC вышел в марте 2009 года, а в январе 2011 года вышел уже ASP.NET MVC 3. В целом могу сказать, что ASP.NET MVC удался и, хотя он достаточно молод, на нем уже вполне можно реализовывать коммерческие приложения.

Кстати сказать, у нас в ФогСофт уже реализован один коммерческий проект на ASP.NET MVC 2 (был еще один, но он слабо зависел от MVC). И в завершающей фазе (тьфу-тьфу-тьфу :) второй, на ASP.NET MVC 3. Правда, он мало использует функционал именно третьей версии и лишь слегка движок Razor, который прикрутили недавно для печатных форм (начинали проект еще до Release Candidate).

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

Scaffolding

Хоть я и не большой любитель пользоваться англоязычными терминами в русском тексте, буду использовать “scaffolding”, потому что не встретил подходящего перевода. Под scaffolding (применительно к разработке) – обычно понимают генерацию CRUD-логики (Create/Read/Update/Delete) и представления.

Со слов тех, кто продвигает ту или иную реализацию scaffolding иногда может показаться, что мы нажимаем “большую кнопку” и готово работающее приложение. В реальности это немного не так – сгенерированные представления необходимо “доводить до ума”. В чистом виде, на мой взгляд, их можно использовать только для административного интерфейса или прототипов (в случае, когда заказчику гораздо важнее бюджет, нежели красота и удобство работы).

Почему же я вообще начал говорить об этом? На это есть несколько причин:

  • Тема статьи :) Серьезно, если не говорить о scaffolding, то в MVC 3 Tools Update особо не о чем говорить.
  • Сгенерированные представления, тем не менее, вполне пригодны к использованию, если подразумевается их доработка.
  • Для тех, кто мало знаком с ASP.NET MVC, Entity Framework или Razor, сгенерированные представления предоставят, в некотором роде, стартовые примеры, причем основанные на используемой предметной области.

Историческая справка – есть альтернатива для scaffolding’а и в Web Forms – Dynamic Data.

MVC 3 Tools Update

Как я уже сказал, недавно вышло обновление MVC 3 Tools, которое включает в себя следующие основные улучшения:

  • Поддержка scaffolding – о нем речь пойдет далее.
  • Intranet project template – новый шаблон проекта, в котором нет контроллеров и представлений для “Account” и настроена Windows-авторизация. Все это можно без особых проблем сделать самостоятельно, но когда есть такая опция все же приятно.
  • Скрипты через NuGet – удобный способ обновить, скажем, версию jQuery.
  • Поддержка HTML5 – подключен Modernizr 1.7 и в диалоге создания проекта есть флажок для включения поддержки HTML5, только не ожидайте, что от его включения приложение сразу станет в два раза круче выглядеть :)

Сразу скажу о маленьких каплях дегтя в этом бочонке с медом:

  • Для унаследованных моделей неправильно работает генерация – код не компилируется (для кого-то это может быть важным, для кого-то – не очень).
  • Те, кто пользуется Resharper 5.x не увидят нормальной поддержки Razor – все будет работать только в шестой версии. Строго говоря, это не проблема MVC 3 Tools Update, но лично меня это расстраивает :)
  • Нельзя создавать проекты ASP.NET MVC 3 в “Solution folder” (невелика потеря, но если сильно нужно – можно сначала создать проект, а потом его перенести).

Что еще важно – для тех, кто уже использует ASP.NET MVC 3 в своих проектах, обновление не несет никаких серьезных изменений, которые потребовали бы дополнительных затрат (тестирование и т.п.). Причина в том, что сам фреймворк остался неизменным – изменения коснулись только инструментов Visual Studio 2010.

Entity Framework 4.1

Перед тем, как рассказывать про Code First, позволю себе упомянуть одну незаметную на его фоне, однако тоже важную новую возможность – типизированный “Include”. Для тех, кто еще мало сталкивался с Entity Framework, поясню – метод “ObjectQuery<T>.Include” позволяет сразу подгружать родительские объекты по ссылающимся на них свойствам (с точки зрения SQL происходит JOIN).

Так вот, в предыдущих версиях “Include” работал только со строками, то есть без строгой типизации. Теперь, наконец-то можно (не подключая сторонних библиотек или CTP-версий) использовать, например, такой синтаксис:

query.Include(e => e.Level1Reference.Level2Collection);

Подробнее про варианты использования Include можно прочитать в описании этого метода.

Теперь о том, ради чего, по большому счету, был выпущен Entity Framework 4.1 – о Code First. Как следует из названия, мы можем сначала описать сущность в виде POCO-класса, а затем не только использовать этот класс для работы с базой данных, но и сгенерировать структуру соответствующих таблиц в ней.

В конце предыдущего предложения многие поставили бы знак восклицания, а некоторые и не один. Я ограничусь точкой. Сейчас объясню почему, только сначала небольшое лирическое отступление.

Под POCO (Plain Old CLR Object), как вы наверняка знаете, подразумевается класс, не обремененный наследованием или атрибутами, необходимыми для работы каких-либо фреймворков. Поскольку чаще всего POCO упоминают в контексте “persistence ignorance”, то под фреймворками обычно понимают ORM.

Теперь обещанное объяснение. Хочу, чтобы вы меня поняли правильно – мне нравится Code First вообще (хотя применимость зависит от проекта) и Entity Framework 4.1 в частности. Однако мне не очень нравятся восторженные отзывы, умалчивающие некоторые подробности, а именно:

  • POCO, строго говоря, подразумевает отсутствие наследования и атрибутов. Если мы по правилам Code First создадим такой класс и используем его для генерации структуры таблицы, то увидим, что строковому свойству будет соответствовать “нулабельный” столбец типа “nvarchar(MAX)”. Вероятно, не все будут в восторге от такого варианта. Это можно обойти с помощью ModelBuilder (Fluent API для настройки сущностей), однако такой подход может быть удобен не для всех.
  • Генерация структуры работает пока только для Microsoft SQL Server (другие провайдеры, например для Oracle пока в бете, хотя что-то вроде есть у DevArt).
  • На случай, если кто-то этого ожидал – нет поддержки частичной перегенерации структуры. Иными словами, мы можем только удалить БД и создать ее заново. Обратите внимание – если будете использовать генерацию БД, не забудьте отключить ее при развертывании новых версий у заказчика.
  • Pluggable conventions сделают, скорее всего, в следующем обновлении. Это значит, например, что мы не можем определить правило, по которому всем строковым свойствам с названием Name будет соответствовать обязательный столбец с длиной 50 символов.
  • Поддержки enum по-прежнему нет. Не то чтобы это краеугольный камень ORM… Только почему такой не особо сложный и давно запрошенный функционал до сих пор не реализован?! Вот здесь позволю себе знак восклицания :) Честное слово, я не любитель фраз вида “WTF”, но по прошествии такого времени очень напрашивается.
  • Есть менее критичные с моей точки зрения нереализованные в Entity Framework 4.1 возможности, которые для кого-то могут быть критичными. Только хочу напомнить, что речь идет про Code First, если вы используете предыдущую версию (Entity Framework 4), то после установки обновления у вас вряд ли что-либо сломается :)

В итоге, мое мнение об Entity Framework 4.1 следующее:

  • Вполне можно использовать, отдавая себе отчет, что мы не в сказке, и получить приемлемую структуру базы данных на основе “чистых” POCO-классов не получится. Если важна чистота классов, рекомендую посмотреть на вышеупомянутый ModelBuilder.
  • Когда цель – быстро получить прототип – можно использовать даже “чистые” POCO-классы, особенно, вкупе с MVC 3 Scaffolding, только нужно понимать, что получится очень далекий от реального приложения прототип.
  • Если база данных уже есть (причем развернута у заказчика) то Code First по-прежнему можно использовать, только, разумеется, без генерации структуры таблиц. Причем можно временно построить edmx-схему на основе структуры БД, а потом, на ее основе, сгенерировать POCO-классы.

Scaffolding в MVC 3

Соединяем все вместе. После установки MVC 3 Tools Update у нас появляется возможность создать класс вида:

public class Test
{
public int Id { get; set; }

[Required, StringLength(50), RegularExpression
(@"\w+", ErrorMessage =
"The name must contain only letters and digits.")]
public string Name { get; set; }
}

После чего, вызвав по правой кнопке диалог по добавлению контроллера:

SNAGHTML22e5f69

Мы сможем выбрать только что созданный класс в качестве модели для генерации контроллера и представлений:

SNAGHTML1f3098b

При создании можно выбрать (см. подчеркнутый выпадающий список):

  • контроллер с CRUD-логикой и представлениями (то, о чем и шла речь);
  • контроллер с пустыми методами без привязки к модели (видимо, когда хочется написать их содержимое по максимуму руками);
  • пустой контроллер (это нам сейчас совсем неинтересно).

Посмотреть слайды более подробно на примеры использования можно будет в следующей статье. А пока отмечу, что генерация несвязанных таблиц и связей one-to-many работает без особых проблем (при создании и редактировании показываются выпадающие списки для ссылок на родительские строки). 

Добавлю также, что вместе с ASP.NET MVC 3 вышел пакет MvcScaffolding, в котором, в том числе, есть возможность создавать свои шаблоны. Если будет время – посмотрю на него и расскажу в следующей статье.

Резюме

Притом, что Scaffolding в MVC 3 и Entity Framework 4.1 нельзя назвать совершенным, его можно использовать, причем не только для простейших прототипов и административного интерфейса. Просто для реальных приложений нужно будет все-таки поработать головой и руками. Как, впрочем, и всегда :)

Если у вас есть замечания, пожелания или новые темы – пишите в комментариях или на olegaxenow.reformal.ru. Постараюсь учесть.

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

  1. Огромное спасибо за статью! Если вам будет интересно, расскажите о Data Annotations и Entity Framework Code First. Очень интересно было бы почитать о связывании моделей.

    ОтветитьУдалить
  2. Анонимный11 мая 2011 г., 9:53

    Присоединяюсь к первому посту. И ещё хотелось бы услышать про реализацию many-to-many в MvcScaffolding?

    ОтветитьУдалить
  3. Выражусь на счет Entity Framework: довольно таки симпатичная и удобная ORM, но для не особо сложной доменной логики, т.к. до сих пор нет поддержки IEnumerable и доступа к приватным полям класса, что довольно таки критично, когда нужно контролировать логику добавления в коллекцию.

    ОтветитьУдалить
  4. У blogger сегодня был какой-то сбой и потерялась пара комментариев, но в gmail у меня они остались :)
    Поэтому на случай если не восстановят напишу комментарий Михаила, а потом мой ответ на все.

    *Михаил*
    Выражусь на счет Entity Framework: довольно таки симпатичная и удобная ORM, но для не особо сложной доменной логики, т.к. до сих пор нет поддержки IEnumerable и доступа к приватным полям класса, что довольно таки критично, когда нужно контролировать логику добавления в коллекцию.

    ОтветитьУдалить
  5. Всем спасибо за комментарии :)

    To @Dzmuh
    Скорее всего расскажу про Code First, вероятно достаточно будет следующей статьи с примером его использования. Что именно подразумевается под связыванием моделей?

    To @Анонимный
    Many-to-many в пакете MvcScaffolding пока вроде нет (или я не заметил?), возможно расскажу про реализацию many-to-many вообще, но позже.

    To @Михаил
    Учитывая, что 4-я версия стала значительно приятнее, у меня большая надежда на 5-ю :)
    Можно подробнее, о каком контроле идет речь?

    ОтветитьУдалить
  6. поправьте опечатку - "read" а не "reade" в расшифровке "CRUD

    ОтветитьУдалить
  7. Анонимный15 мая 2011 г., 13:16

    Scaffolding равносилен генерации hello world. Как по мне, так наличие средств для генерации таких примитивных сущностей указывает на то, что они сложнее чем должны быть.

    Во вторых - бизнес логику удобно хранить в отдельной dll, т.к. это позволяет не привязываться к реализации интерфейса (будь то html, wpf, sl, консоль или сервис). Смысл буквы M просто теряется. К чему так ръяно следовать руби-стайлу, непонятно.

    Реализация view у современных MVC всеравно черезвычайно убога. Хранить html кусками... вперемешку с логикой... брр, какая гадость. Немногим лучше чем просто генерировать html StringBuilder'ом. А может даже и хуже. Web.forms тоже не идеален, но хотябы есть возможность код, большей частью, вынести в codebehind. Вообще, я думаю, html должен хранится в первозданном виде. В том виде, в котором он пришел от верстальщика.

    В третьих - Entity Framework хоть и универсальный, но в тоже время невероятно медленный. Далеко на нём не уедешь.

    ОтветитьУдалить
  8. To @Dmitry

    Спасибо! Fixed.

    To Анонимный:

    >Scaffolding равносилен генерации hello world

    Для тех задач, про которые я написал, что он хорош, он действительно хорош. Можно придумать больше. Плюс к этому многие забывают, что никто не мешает дорабатывать сгенерированный код (лишнего там особо нет). Впрочем, более интересное про Scaffolding я расскажу дальше.

    > Во вторых - бизнес логику удобно хранить в отдельной dll

    А кто мешает это делать в MVC?

    > Реализация X в Y всеравно черезвычайно убога.

    Специально процитировал в таком виде чтобы было понятно, почему не хочу дискутировать на этот счет :)

    > В третьих - Entity Framework хоть и универсальный, но в тоже время невероятно медленный. Далеко на нём не уедешь.

    Если был бы *невероятно* медленный, то не, так мои коллеги наверное заметили бы. Согласен, если делать что-то вроде StackOverflow - то да, нужно что-то придумывать. Тот же Dapper, например.

    ОтветитьУдалить
  9. Анонимный20 мая 2011 г., 10:21

    > Генерация структуры работает пока только для Microsoft SQL Server (другие провайдеры, например для Oracle пока в бете, хотя что-то вроде есть у DevArt).

    У DevArt генерация структуры БД заявлена для Oracle, MySQL, PostgreSQL и SQLite. См. их страницу, посвящённую EF:
    http://www.devart.com/dotconnect/entityframework.html
    (цитата оттуда: "Dynamic database creation and deletion support. dotConnect data providers support Dynamic database creation and database deletion for both Entity Framework v1 and v4.")

    ОтветитьУдалить
  10. Анонимный14 июля 2011 г., 23:30

    Как на счет поддержки двойных ключей (уникальность по двум полям, связь многие ко многим), а то в EF-4.0

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