Примечание. Эту статью я написал еще в 2016 году, но остался в черновиках. Хотя .NET MVC Core сейчас является значительным шагом вперед по сравнению с .NET MVC, мои замечания все же заслуживают внимания.

Языки программирования со статической типизацией - не самые простые. Мы видим, что студенты легко усваивают Python, но борются с Java, когда знакомятся с типами, операторами объявления и всей этой ерундой. Сравните, например, примеры Hello World:

Джава:

public class HelloWorld {
    public static void main(String[] args) {
         System.out.println("Hello World!");
    }
}

Python:

print(“Hello World”)

Пример Java представляет нам три различных типа: класс HelloWorld, «void» и «String []», но с примером Python нам даже не нужно знать, что это за тип. Код Java излишне выглядит громоздким для выполняемой функции. Конечно, весь мир не об этом.

Я писал веб-код, используя VBScript, когда он был де-факто альтернативой PHP. Он был очень похож на Visual Basic, но был динамически типизирован, что означало, что переменная могла быть без ошибок повторно присвоена значению другого типа. Таким образом, приведенный ниже код будет работать нормально:

a = 2            ' Integer
a = "test"       ' String
b = a            ' String
b = 2 = "test"   ' Boolean

Он не был строго типизированным как таковым, но имел правильные проверки и измерения в нужных местах, такие как явные функции преобразования типов, отдельный оператор конкатенации строк, 2 + “test" ошибки выдачи и т. Д.

VBScript был кратким, легко читаемым, но сильно ограниченным. У него даже не было встроенного класса хеш-таблицы, который есть почти во всех платформах программирования. Вам нужно было создать экземпляр COM-объекта, чтобы получить доступ к аналогичным функциям.

Одной из интересных особенностей VBScript была возможность изменять режим обработки ошибок на лету с помощью двух операторов, называемых On Error Resume Next и On Error Goto 0. Это были артефакты из старых времен BASIC, когда в исходном коде были номера строк.

Если вы выбрали «возобновить следующий», VBScript просто проигнорировал ошибки и продолжил. Вы все равно можете проверить, была ли ошибка явным образом, с помощью оператора if. Если вы выбрали GOTO 0, программа остановится с сообщением об ошибке при первой обнаруженной ошибке.

Невежество - это блаженство, иногда

Игнорирование ошибок имеет интересные последствия для веб-разработки. Возможно, вы не захотите делать это в коде обработки платежа по кредитной карте. Хотя иногда это может быть даже желательно, особенно когда ваша страница состоит из множества более мелких частей. Вы определенно не хотите, чтобы маленькая неважная рамка в углу страницы мешала всему остальному. Вот почему я считаю свободную семантику обработки ошибок в языке Go подходящей для веб-разработки. Если бы вы не справились с ошибкой, мир не рухнул бы и не сгорел бы, как с исключениями. Вы хотите, чтобы в сети было такое же терпимое отношение к ошибкам. Поэтому я использовал оба оператора VBScript в зависимости от того, насколько важной была часть страницы, созданная кодом. Иногда даже на функциональном уровне:

function doSomethingVeryCritical(value)
  on error goto 0
  ' do something very critical here 
  on error resume next ' hopefully the last guy wanted this
end function

Самая большая проблема с моделью «возобновить следующее» - это простые синтаксические ошибки, в основном опечатки, благодаря динамической типизации. Я смог понять их только тогда, когда действительно протестировал веб-страницу. У VBScript не было красивой IDE, только несколько текстовых редакторов с подсветкой синтаксиса. Вы не знали, правильное имя переменной или нет. Если бы вы проигнорировали ошибки, вы даже не смогли бы сказать, есть ли проблема, потому что эта часть просто не была отрисована. Из-за ошибки или из-за простого решения? Если бы я не проигнорировал ошибки, вся страница была бы сломана из-за клавиши, которую я нажал случайно.

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

Разработка быстрых веб-приложений

Мне нравится C #, потому что он кажется такой, какой должна была быть Java с самого начала. Хорошая стандартная библиотека, свойства, асинхронность, лямбда-выражения, LINQ, быстрый JIT, современный сборщик мусора. Все хорошие вещи, причем как статически типизированные, так и строго типизированные.

Статическая типизация - очень хорошее дело. Вы жертвуете одним временем «компиляции» своего кода и внезапно получаете все эти преимущества: более быстрый код, раннее обнаружение несовместимости типов и отсутствие опечаток! Блин, я ненавижу опечатки. На самом деле я должен определить «статически типизированный» как это в The Straightforward Encyclopedia of Programming:

Статически типизированный (концепция)
Быстрый язык программирования, и опечатки больше не являются проблемой.

C # когда-то был отличным языком для разработки под Windows, но не для Интернета. В основном это связано с моделью разработки ASP.NET WebForm. Он включал в себя набор пользовательских компонентов, предметно-ориентированный язык, подобный HTML, и модель программирования пользовательского интерфейса, адаптированную из настольного программирования, которое вам нужно было изучить, в дополнение к HTML, CSS и JavaScript. Я уверен, что ASP.NET WebForm тоже пережил популярные дни, но для Интернета он совсем не подходит.

К счастью, в 2009 году была выпущена платформа ASP.NET MVC Framework. Это совсем другой зверь, хоть и поменьше. Это механизм модель-представление-контроллер, основанный на том, что представил Ruby On Rails. Поскольку его название слишком длинное, я с годами отказался от его частей: сначала ASP, затем Framework. В настоящее время я в основном называю это просто .NET MVC.

Мне нравится .NET MVC и то, чем он становится. Он неуклонно превращается в возможную кроссплатформенную технологию. Наша социальная платформа Ekşi Sözlük уже много лет работает на нескольких серверах, обеспечивая около 350 миллионов просмотров страниц в месяц.

Но…

Этого не достаточно!

Я уже упоминал, что не люблю опечатки? Одним из недостатков .NET MVC является то, что он привносит несколько слабо типизированных конструкций в рабочий процесс веб-разработки. Это:

Составление карты маршрута

Подход MVC умело отделяет то, как выглядят ваши URL-адреса и как вы к ним обращаетесь. Таким образом, изменение схемы URL-адресов в вашем веб-приложении не нарушит существующий код, который должен создать его версию. Сопоставление URL-адреса и ваших контроллеров называется «маршрутизацией». Вам не нужно запоминать точный формат URL.

Вместо того:

<a href="/some/url/to/my/action">link</a>

Вы бы написали:

<a href="@Url.Action("Action", "Controller")">link</a>

Таким образом, URL становится тем маршрутом, на который был сопоставлен метод Controller.Action. Вам нужно было только знать, к какому контроллеру и действию вы должны обратиться. Ваше действие будет выглядеть так:

public class Controller
{
  public ActionResult Action()
  {
     // important stuff
  }
}

Обычно строки "Action" и "Controller" будут относиться к методу и имени класса соответственно. Но зачем нам передавать их строками. Что, если кто-то случайно сделает ... тёпо?

Разработчики .NET MVC подумали об этом и представили лямбда-выражения в его ранних выпусках. Если вы сделаете опечатку, компилятор остановится с ошибкой. Если я правильно помню, синтаксис выглядел примерно так:

<a href="@Url.Action<Controller>((c) => c.Action())">link</a>

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

К счастью, кто-то создал шаблонное решение T4 под названием T4MVC, которое преобразует выражения в имена контроллеров и действий во время компиляции. Для этого требуется дополнительный проход шаблона во время компиляции, поэтому это не самое быстрое решение, а настройка настраиваемых макетов MVC может быть сложной. Я определенно предпочел бы нестандартное решение моей проблемы с опечаткой.

ViewData

В архитектуре MVC «модель» представляет объекты, которые вы используете для передачи данных. Когда вы отправляете форму, данные поступают в действие вашего контроллера в виде «модели». Когда вы отображаете таблицу значений, они передаются в представление в «модели». Поскольку для обоих использовался один и тот же термин, программисты также склонны использовать один и тот же объект для обоих.

Повторное использование объектов модели создало новую проблему. Что, если мне понадобятся дополнительные, но не относящиеся к делу данные в представлении? Скажем, я хотел показать количество непрочитанных сообщений в дополнение к тому, что я показываю. Я не могу поместить это в существующую модель «UserRegistrationForm». В непрочитанных сообщениях нет смысла.

Правильный способ - создать новый класс модели, который в просторечии называется «ViewModel», и передать в него дополнительные данные вместе с существующей моделью. Типичный, безупречный, без опечаток. Но мы знаем, что, как и пользователи, программисты тоже выбирают более короткий путь:

Более короткий путь здесь - конструкция под названием «ViewData». ViewData - это словарь, в который вы можете добавить все, что захотите. Итак, вы хотели передать вашему представлению «Непрочитанные сообщения»? Вы бы сделали это в своем методе действий:

public ActionResult Action()
{
  var model = ...
  ViewData["UnreadMessages"] = unreadMessageCount;
  return View(model);
}

По вашему мнению, вы получите доступ к данным таким же образом:

<span class="unread">@ViewData["UnreadMessages"]</span>

Как вы уже догадались, любая опечатка оставит поле пустым, как VBScript с включенным On Error Resume Next. Почувствуйте ветер 1999 года на своем лице, когда «Калифорния» была хитом, а аэропорты были довольно хорошими.

ViewBag

Команда MVC думала, что ViewData["UnreadMessages"] слишком сурово относится к разработчикам, и хотела сделать его короче, поэтому они создали ViewBag. ViewBag аналогичен ViewData. Все, что вы добавляете в ViewData, вы можете прочитать из ViewBag и наоборот. Но это динамический объект, который в The Straightforward Encyclopedia of Programming описывается как:

Динамический объект (тип объекта)
Объект, которому не важны типы, компиляторы или вы.

Таким образом, вы получаете доступ к членам ViewBag как к обычному объекту:

ViewBag.UnreadMessages

Но это не значит ничего. Это просто для программистов, которым не нужно набирать скобки и двойные кавычки вместе. Если бы вы допустили опечатку, компилятор не узнал бы об этом. Он не мог вас предупредить. И, в отличие от ViewData, вы бы даже не знали, что компилятор не знает. Потому что он выглядит как строго типизированный объект!

ViewBag плох не только потому, что это слабо типизированная, подобная словарю конструкция, но и потому, что доступ к динамическим объектам медленнее, чем доступ к обычному словарю. Это медленный, безобидный, злой брат ViewData.

Анонимные объекты

На самом деле это лучшие родственники динамических объектов, используемых в .NET MVC. Анонимные объекты строго типизированы и используются для передачи пар ключ / значение без необходимости создания сложного словаря или нового класса. Допустим, вам нужно передать ссылки HTML-атрибуты:

@Html.ActionLink("caption", "Action", "Controller", 
    new { style="display:none" })

Если вы не использовали анонимный объект, вам пришлось прибегнуть к:

@Html.ActionLink("caption", "Action", "Controller",
    new Dictionary<string, string>()
    {
        { "style", "display:none" } 
    })

Версия анонимного объекта намного короче, не так ли? Они называются анонимными, потому что их базовые типы получают неясное имя компилятором во время компиляции. Поскольку им не присвоено имя до тех пор, вы не можете повторно использовать эти типы и напрямую обращаться к их членам, только используя отражение во время выполнения.

Я не против, потому что они вообще не выглядят строго типизированными. Их нельзя повторно использовать или получить к ним доступ где-либо еще. Идеально подходит для одиночного использования. Вам не нужен быстрый доступ к объекту Dictionary, потому что содержимое только перечисляется.

Обними свою силу

Существует множество зрелых фреймворков MVC на языках со слабой типизацией, таких как PHP, Python, Ruby и т. Д. Языки со строгой типизацией, такие как Go и Crystal, демонстрируют многообещающую производительность, но не имеют простых в использовании высокоуровневых фреймворков.

Напомню, что на этапе компиляции, обеспечивающем строгую типизацию, устраняются тонкости динамической среды со слабой типизацией, а именно более быстрое развертывание, плавная кривая обучения, быстрое прототипирование, более быстрые циклы редактирования и выполнения. .NET MVC должен их компенсировать.

Фреймворк находится в начале новой фазы своего развития, ранее называвшейся ASP.NET MVC vNext, а теперь кроссплатформенной альтернативой ASP.NET Core MVC. Я думаю, что через пару лет он станет стандартом, и мы все забудем о существовании отдельной версии для Windows.

Это дает возможность команде .NET MVC понять ее строго типизированные корни. Мои три пожелания джинну из команды .NET MVC:

  • Утилизируйте ViewBag. Это должно быть наивысшим приоритетом, поскольку в настоящее время это больше всего вредит циклу разработки.
  • Сохраняйте ViewData, поскольку это служит практическим целям. Хотел бы я придумать столь же простую и строго типизированную альтернативу, но я согласен с тем, что она явно и явно слабо типизирована, в отличие от ViewBag.
  • Отменить синтаксис сопоставления маршрутов со слабой типизацией. Сделайте его строго типизированным без необходимости проходить шаблон.

Разработчики, Разработчики, Разработчики

Я предлагаю веб-разработчикам, использующим .NET MVC, выбрать ViewData вместо ViewBag. Вы даже можете реализовать свойства в производном от RazorViewBase, который обеспечивает полный доступ к ViewData, чтобы сделать вещи строго типизированными, по крайней мере, на стороне представления. Если вы не можете этого сделать, просто соберите все элементы ViewData в один класс и получите к нему доступ с помощью одного ключа. Это уменьшит вероятность опечатки. Если это звучит как утомительная работа, придерживайтесь самой ViewData. Для строго типизированной маршрутизации попробуйте T4MVC. Посмотрите, приемлемы ли его накладные расходы на компиляцию.

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