Друзья, перед вами второй выпуск колонки про ASP,NET5, в которой мы знакомимся с разными интересными вещами из мира веб-разработки на новой версии открытой платформы ASP.NET5.


В прошлый раз мы говорили про новые подходы в работе со статическим контентом на платформе. В комментариях возникло предложение поговорить в следующих выпусках про азы для веб-разработчиков, которые только начинают пользоваться ASP.NET и погружаться в тему. Мы прислушались к вам и предлагаем в этом выпуске материал от Андрея Веселова ( StealthDogg) – эксперта веб-разработки, автора множества статей по теме ASP.NET и Microsoft MVP.

Встречайте введение в азы ASP.NET5 – контроллеры, представления и модели.
Примечание. Данная статья актуальна для финальной версии Visual Studio 2015 с ASP.NET5 Beta5. Для обновления на новые версии ASP.NET воспользуйтесь примечаниями к релизу.

Работа с контроллерами


Давайте посмотрим на работу контроллера и действий шаблона MVC на примере небольшого веб-приложения.
aspnetcolumngithubСовет! Вы можете попробовать все самостоятельно или загрузив исходный код из GitHub https://github.com/vyunev/AspColumnControllerDemo.

Создаем проект


Для этого создадим проект ControllerDemo (Рис.1).


Рис.1 – Создание проекта веб-приложения ASP,NET5

В настройках проекта выберем шаблон Empty из раздела ASP.NET 5 Templates (Рис. 2).


Рис.2 – Выбор шаблона веб-приложения

После создания проекта в Solution Explorer будут отражены следующие элементы (Рис. 3).


Рис.3. – Структура шаблона

Теперь необходимо подключить ASP.NET MVC 6 в проект.

1. В файле project.json укажем зависимость от библиотеки Microsoft.AspNet.Mvc.

"dependencies": {
    "Microsoft.AspNet.Server.IIS": "1.0.0-beta5",
    "Microsoft.AspNet.Server.WebListener": "1.0.0-beta5",
    "Microsoft.AspNet.Mvc": "6.0.0-beta5"
},

2. Затем необходимо включить ASP.NET MVC 6 в конвейер Owin для обработки запросов и настроить маршрутизацию. Для этого в файле Startup.cs добавим следующие строки:

public class Startup
{
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller}/{action}/{id?}",
                    defaults: new { controller = "Home", action = "Index" });
            });
        }
}

Проект подготовлен и можно приступать к созданию контроллеров и действий.

Контроллеры и действия


Как правило, контроллеры размещают в специальной папке Controllers. ASP.NET MVC 6 позволяет использовать и другие папки, т.к. ищет их по базовому классу. Однако в данном примере остановимся на классическом варианте расположения файлов. С помощью контекстного меню создадим папку Controllers ("Add > New Folder") и в нее новый класс HomeController ("Add > New item… " и выберем Class).

Примечание. Имя класса контроллера всегда состоит из имени контроллера (в данном случае "Home") и окончания "Controller".
Совет. В дальнейшем для создания контроллеров можно использовать готовый шаблон MVC Controller Class. Но сейчас, в целях изучения, создадим обычный пустой класс.

Базовым классом для всех контроллеров веб-приложения является Controller. Поэтому унаследуем созданный класс от него:

public class HomeController : Controller
{
}

Класс Controller является не только маркером контроллеров, но предоставляет основную функциональность для работы с полученным запросом и формирования ответа. В данный момент стоит отменить такие его свойства, как:

  • ViewBag и ViewData — предназначены для передачи произвольных данных в представления;
  • Request — содержит данные исходного запроса;
  • Response — позволяет создать ответ для клиента.

Кроме того, он предоставляет рад полезных методов, которые будут рассмотрены чуть позднее.

За непосредственную обработку запроса в контроллере отвечает действие, к котором можно выделить следующие части:

  1. Указание конкретного типа запроса, используя атрибуты [HttpGet], [HttpPut], [HttpPost], [HttpPatch], [HttpDelete]. Так же можно указать несколько типов, используя [HttpMethod]. При этом запросы остальных типов будут игнорироваться. Если атрибуты отсутствуют, то действие будет вызвано для любого запроса.
  2. Результат действия — его тип это реализация IActionResult. Все действия должны вернуть экземпляр класса, реализующий указанный интерфейс. По сути, он отвечает за формирование ответа, который будет отправлен клиенту. Это может быть HTML страница, файл, переадресация или код ошибки.
    Базовый класс Controller содержит методы, подходящие в большинстве стандартных случаев:

    1. View() — создание ответа (html-страницы) с использованием представления;
    2. Content() — предназначен для передачи произвольной текстовой строки клиенту, например, это может быть сгенерированный CSS или JavaScript;
    3. File() — позволяет вернуть клиенту файл;
    4. Json() — преобразует объект к формату JSON и возвращает его клиенту;
    5. Redirect(), RedirectPermanent(), RedirectToAction(), RedirectToRoute() — переадресуют клиента по новому адресу.

  3. Название действия, по которому будет происходить его вызов. По умолчанию считается, что строка запроса соответствует формату: www.site.com/<Controller>/<Action>
  4. Метод действия может иметь параметры. Значения для них ASP.NET MVC постарается взять из строки запроса. Сопоставление параметров действия и запроса осуществляется по именам.
  5. И наконец, тело метода, в котором подготавливаются необходимые данные и возвращается результат.

Настало время добавить первое действие — Index:

public class HomeController : Controller
{
    // GET: /Home/
    public IActionResult Index()
    {
            return this.Content("Hello ASP.NET MVC 6.");
    }
}

Если теперь запустить веб-приложение, то заданная строка "Hello ASP.NET MVC 6." будет отображена в браузере.

Давайте разберем что именно произошло:

  • Браузер отправил на сайт запрос вида http://localhost:[port]/
    * Примечание: Порт для запуска в режиме отладки автоматически выбирается при создании проекта из свободных.
  • Поскольку ни контроллер, ни действие явно не указаны, то маршрутизация ASP.NET MVC использует варианты заданные по умолчанию: Home и Index.
  • Был создан экземпляр класса HomeController.
  • У него было найдено и выполнено действие Index.
  • Метод Index() вызвал метод Content() для формирования ответа, состоящего из указанной строки.
  • Механизм ASP.NET MVC получил ответ и переслал его браузеру клиента.
  • Браузер отобразил полученную в качестве ответа строку.

Если запрашиваемый контроллер или действие не будут найдены, то будет отправлен ответ "404. Страница не найдена".

Немного усложним и создадим конструктор класса и еще одно действие:

    public class HomeController : Controller
    {
        private readonly string _time;

        public HomeController()
        {
            this._time = DateTime.Now.ToString("G");
        }

        // GET: /Home/
        public IActionResult Index()
        {
            return this.Content("Hello ASP.NET MVC 6");
        }

        // GET: /Home/Echo/
        public IActionResult Echo(string message)
        {
            return this.Content($"{this._time} > {message}");
        }
    }

Теперь в качестве ответа будет возвращаться время создания контроллера и переданная в запросе строка. Это позволит убедиться, что новый экземпляр HomeController создается при каждом запросе.

Для вызова созданного действия необходимо обратиться по пути, включающий имена контроллера и самого действия. Запустим веб-приложение и введем в браузере следующий адрес:

http://localhost:[port]/Home/Echo?message=Hello

Асинхронность — это просто


Рассмотрим, как легко ASP.NET MVC 6 позволяет создавать асинхронные методы. Переделаем действие Echo. Для симуляции вызова некого асинхронного метода воспользуемся Task.Delay():

public async Task<IActionResult> Echo(string message)
{
      await Task.Delay(100);
      return this.Content($"{this._time} > {message}");
}

Потребовалось только заменить тип результата с IActionResult на Task<IActionResult> и добавить модификатор async. Такое действие может использовать вызовы асинхронных методов, например, для обращения к различным источникам данных или сервисам.

Немного практики


И в завершении, два небольших практических задания:

  1. Напишите действие, так же выводящее значение параметров, но которые передаются запросе только типа POST.
    Подсказка. Для формирования запроса можно использовать обычную HTML страницу с формой. Её файл разместите в уже существующей папке wwwroot, которая предназначена для любого статического содержимого сайта (html страницы, файлы и т.д.)
  2. Создайте действие, которое отвечает только на Get запрос и:
    • возвращает файл;
    • возвращает объект в формате Json (для простоты можно использовать анонимный класс);
    • переадресует на другую страницу.

И не спешите заглядывать в приложенный исходный код, где есть все решения. Постарайтесь сделать это самостоятельно.

Работа с представлениями


Перейдем к рассмотрению следующей составляющей шаблона MVC — представлений.
aspnetcolumngithubСовет! Вы можете попробовать все самостоятельно или загрузив исходный код из GitHub https://github.com/vyunev/AspColumnViewDemo.

Создаем веб-приложение с представлением


Для этого создадим пустое ASP.NET MVC веб-приложение ViewDemo, аналогично тому как это было сделано в предыдущей части. Только в этот раз контроллер HomeController будет выглядеть следующим образом:

    public class HomeController : Controller
    {
        // GET: /Home/
        public IActionResult Index()
        {
            return this.View();
        }
    }

Метод View() создает результат действия, используя представление. Попробуем запустить проект и сразу получим сообщение об ошибке, т.к. в проекте еще нет представления для действия Index (Рис. 4).


Рис.4 – Сообщение об ошибке при отсутствующем представлении

Если внимательно прочитать текст сообщения, то можно заметить, что для представлений существует четко определенное место в структуре проекта. Это папка Views. В ней размещаются вложенные папки с именами соответствующими именам контроллеров. В данном примере это будет Home. И уже в ней хранятся файлы представлений, имена файлов которых, в общем случае, совпадают с названиями действий, для которых они предназначены.

Кроме того, есть специальная папка Views/Shared. В ней размещаются представления доступные для любых действий в проекте.

Создадим указанные выше папки Views и Home. После этого, в последней, с помощью пунктов контекстного меню "Add > New Item …", добавим представление (MVC View Page) с именем Index.cshtml. Расширение cshtml показывает что будет использоваться язык разметки Razor. В результате проект будет выглядеть следующим образом (Рис. 5):


Рис.5 – Структура проекта с новым представлением

Добавим в Index.cshtml следующий код:

<h1>View Demo</h1>

Запустим веб-приложение. При этом произойдет следующее:

  1. Будет вызвано действие Index контроллера Home (как это было описано в предыдущей статье).
  2. В действии Index, метод View() будет использовать представление View/Home/Index.cshtml для создания результата (экземпляра класса реализующего IActionResult).
  3. Результат действия сформирует HTML код в ответ клиенту (в методе ExecuteResultAsync интерфейса IActionResult).
  4. Созданная страница будет отправлена клиенту и отображена его браузером.

Метод View() ищет представление, используя следующие пути:

  1. Сначала View/<Имя контроллера>/<имя действия>.cshtml
  2. Затем View/Shared/<имя действия>.cshtml

Если оба поиска ничего не принесли, то выводится сообщение об ошибке.

Существует перегрузка метода, позволяющая явно указывать имя представления. Например, вызов

this.View("MyView") будет искать файл View/<Имя контроллера>/MyView.cshtml или View/Shared/MyView.cshtml. Это может быть полезно, например, если действие должно давать различный результат в зависимости от какого-то условия.

Кроме того, существует вариант View(), позволяющий передавать данные в представление. Это будет подробнее рассмотрено далее. А сейчас немного разберем возможности Razor.

Язык разметки Razor


Базовые конструкции

Razor допускает следующие конструкции:

  • @{ … } — позволяет добавить C# код в тело представления.
  • @переменная, @свойство или @метод() — вставляют строковое значение в HTML код. При этом вывод будет экранирован (т.е. если строка содержит теги, то они будут выведены как обычный текст, а не вставлены как теги).
  • Разрешены такие управляющие конструкции как:
    @if { … } else { … }
    @switch( … ) { …  }
    @for( … ) { … }
    @foreach( … ) { … }
    @while( … ) { … }

В них C# код может быть совмещен с HTML кодом, например:

@for(int i=0; i<10;i++) {
    <p>@i</p>
}

Необходимо отметить, что представление по сути является классом, реализующим IRazorPage, со своими свойствами и методами. Например, доступны ViewBag и ViewData, которые содержат значения в которые были добавлены еще в действии.

Так же существует свойство Html, которое предоставляет большое число вспомогательных методов. Вот некоторые из них:

  • @Нtml.Raw() — добавляет в HTML код текстовое значение без экранирования.
  • @Нtml.CheckBoxFor(), @Нtml.TextBoxFor() и прочие методы для генерации кода различных HTML элементов.

Еще одно вспомогательное свойство — Url. Оно содержит методы для работы с путями в веб-приложении. Наверное, самый востребованный из них это @ Url.Action( … ), который выводит путь до указанного действия указанного контроллера, например: <a href="@ Url.Action("Index", "Home")">Home page</a>.

Необходимо так же отметить специальный файл Views/_ViewStart.cshtml. Его наличие не обязательно, но если он присутствует в проекте, то его содержимое выполняется перед каждым представлением. С его помощью можно задать общий для всех представлений шаблон страниц.

Шаблоны страниц


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

Чтобы не вставлять копию HTML кода страницы в каждый файл представления, можно воспользоваться разметкой (layout). Для этого необходимо:

  1. Создать файл с общей разметкой в View/Shared
  2. В _ViewStart.cshtml или самом действии указывается файл разметки с помощью свойства Layout.
    Подсказка: Если шаблон указан и в файле и в представлении, то будет использоваться тот, что задан в представлении. Таким образом можно задать шаблон по умолчанию в _ViewStart.cshtml, а при необходимости переопределять его в представлении.
  3. Место, где код представления будет вставлен в шаблон, отмечается вызовом @RenderBody().

Итак, в папке View/Shared создадим файл с шаблоном HTML разметки _Layout.cshtml (имя может быть произвольным):

<html>
<head>
    <meta charset="utf-8" />
    <title>@this.ViewBag.Title</title>
</head>
<body>
    <nav>Menu</nav>

    <section id="mainContent">
        @RenderBody()
    </section>

    <footer>Footer</footer>
</body>
</html>

Так же изменим код представления:

@{
    this.ViewBag.Title = "Home page";
    this.Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>View Demo</h1>

Как хорошо видно, данный код:
  • Создает у ViewBag свойство Title со значением "Home page".
  • Устанавливает Layout указывает на используемый шаблон HTML разметки.

Теперь при запуске веб-приложения будет выведена страница с использованием указанного шаблона. В ее заголовок будет вставлена строка "Home page".

Секции


Существует возможность для вставки кода из представления в шаблон страницы вне кода блока, создаваемого @RenderBody(). Это секции, которые работают следующим образом:

  1. В коде шаблона указываются места для вставки секций с помощью @RenderSection(name, required) или @RenderSectionAsync(name, isRequired). Где name определяет уникальное имя секции, а required — является ли она обязательной.
  2. Представления определяют содержимое для секций с помощью конструкции @section name { … }
  3. При отсутствии кода для обязательной секции, будет выведено сообщение об ошибке.

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

Перейдем к примеру. Определим необязательную секцию в шаблоне страницы

<html>
<head>
    <meta charset="utf-8" />
    <title>@this.ViewBag.Title</title>

</head>
<body>
    <nav>Menu</nav>

    <section id="mainContent">
        @RenderBody()
    </section>

    <footer>Footer</footer>

    @RenderSection("scripts", required: false)
</body>
</html>

Теперь зададим код для данной секции в представлении. Для наглядности вызовем alert():

@{
    this.ViewBag.Title = "Home page";
}
<h1>View Demo</h1>

@section scripts {
    <script>
        alert("Demo");
    </script> 
}

Запустим проект и убедимся JavaScript был добавлен на страницу и выполнен.

Частичные представления


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

Использовать частичные представления очень просто:

  1. Создать частичное представление как обычное
  2. Указать в нем Layout = null, чтобы отменить использование шаблона страницы.
  3. Для вставки частичного представления использовать Html.Partial() или Html.PartialAsync().

Как всегда, перейдем к примеру. В новой папке Views/Shared/Forms создадим файл частичного представления_ContactForm.cshtml со следующим содержимым:

<form>
    Message: <input type="text" /><button type="submit">Send</button>
</form>

Выведем его в представлении Index:

@{
    this.ViewBag.Title = "Home page";
}
<h1>View Demo</h1>

@Html.Partial("Forms/_ContactForm")

@section scripts {
    <script>
        alert("Demo");
    </script> 
}

Обратите внимание как задан путь до частичного представления. Его поиск идет по общим правилам, и в данном случае путь указан относительно Shared. Файл с ним можно разместить в любой папке внутри View, он при этом надо будет указывать в параметре метода уже полный путь относительно корня сайта. Например, Html.Partial("~/Views/Shared/Forms/_ContactForm")

Остаётся только запустить проект и убедиться в результате.

Компоненты представлений (View Components)


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

Работаем с моделями


Созданные в предыдущих частях приложения уже содержат контроллер и представление. Остается рассмотреть как использовать еще один компонент шаблона MVC — модели.
aspnetcolumngithubСовет! Вы можете попробовать все самостоятельно или загрузив исходный код из GitHub https://github.com/vyunev/AspColumnModelView.

Создаем модель


Воспользуемся созданным в прошлой части приложением ViewDemo и добавим в него работу с моделью.

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

Модель — это простой объект (POCO). Нет необходимости наследовать его от какого-либо класса или реализовывать какой-либо интерфейс. Создадим модель, которая, для примера, будет содержать имя пользователя и текущее время. Для этого добавим в папку Models файл IndexPageModel.cs с следующим кодом:

using System;

namespace ViewDemo.Models
{
    public class IndexPageModel
    {
        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string FullName => $"{this.FirstName} {this.LastName}";

        public DateTime CurrentTime { get; set; }
    }
}

Модель готова. Обратите внимание, что нет ограничений на класс модели. Например, в данном случае присутствует вычисляемое свойство FullName. Однако не стоит увлекаться и переносить логику приложения в модели. Лучше оставлять их максимально простыми, чтобы облегчить дальнейшее сопровождение приложения.

Дорабатываем контроллер


Итак, модель уже есть. Необходимо создать ее экземпляр и записать в него какие-нибудь значения. Для простоты воспользуемся константами. Перейдём к контроллеру Home и модифицируем действие Index следующим образом:

public IActionResult Index()
{
    var model = new IndexPageModel()
    {
        FirstName = "John",
        LastName = "Doe",
        CurrentTime = DateTime.Now
    };

    return View(model);
}

Для передачи модели в представление достаточно подставить ее экземпляр в качестве параметра метода View(). В данном примере используются константы для заполнения модели. Разумеется в реальном проекте будет обращение к бизнес-логике приложения и полученный результат будет подставлен в модель.

Модифицируем представление


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

@model ViewDemo.Models.IndexPageModel

Обратите внимание, что имя класса указано вместе с его пространством имен.

Теперь можно использовать свойство this.Model, в котором будет содержаться переданная модель, указанного типа. Выведем данные на страницу (полный код представления Index.cshtml):

@model ViewDemo.Models.IndexPageModel
@{
    this.ViewBag.Title = "Home page";
}

<h1>Model Demo</h1>
<p><strong>Имя и фамилия:</strong> @this.Model.FullName</p>
<p><strong>Текущее дата и время:</strong> @this.Model.CurrentTime.ToString()</p>

Запустим приложение. При каждом обновлении страницы будет выводиться указанные в контроллере имя, фамилия, а так же текущие дата и время.

Итак, создавать и использовать модели не сложнее чем обычные классы. Всю работу по передачи ее экземпляра в представление берет на себя ASP.NET MVC. Кроме того, в представлении свойство this.Model становится того же типа, что и модель. Это позволяет использовать IntelliSense при создании и редактировании представлений.

Свежие новости


Как вы уже знаете выпущена Visual Studio 2015 с ASP.NET5 Beta5. Подробности о том, что именно включено в релиз Visual Studio можно почитать в этом блоге.

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

Опубликованы планы по выпуску релизов платформы в течение ближайших месяцев до выпуска финальной версии ASP.NET5. Согласно им нас ждут версии Beta7 и Beta8, после чего в ноябре мы получим первую версию, готовую к продакшну (RC1), финальная же версия выйдет в первом квартале 2016 года. Подробности каждой версии можно найти по ссылке.

Опубликованы доклады конференции DevCon 2015, в том числе по веб-разработке и теме ASP.NET.

Полезные ссылки


Самая свежая документация по ASP.NET5 расположена по адресу http://docs.asp.net/en/latest/.

Приглашаем вас подключаться к живым трансляциям периодического шоу ASP.NET 5 Community Standup, где разработчики из команды Microsoft делятся последними новостями о платформе. Записи доступны по этой ссылке.

Познакомьтесь с новой статьей о разработке ASP.NET-приложений в Visual Studio Code от Эрика Рейтана, в которой подробно изложены интересные аспекты работы с веб-проектами в VS Code.

Изучите основы ASP.NET5 с новым бесплатным курсом виртуальной академии Microsoft.

Авторам


Друзья, если вам интересно поддержать колонку своим собственным материалом, то прошу написать мне на vyunev@microsoft.com для того чтобы обсудить все детали. Мы разыскиваем авторов, которые могут интересно рассказать про ASP.NET и другие темы.

Об авторе


Веселов Андрей
Senior Developer, CodeFirst, Ireland

Сертифицированный разработчик с 15-летним стажем, Microsoft MVP (ASP.Net/IIS). Последние 9 лет занимается веб-разработкой с использованием технологий Microsoft. Сфера основных профессиональных интересов: ASP.NET и Azure. В настоящее время работает в компании CodeFirst в должности Senior Developer. Ведет профессиональный блог.

Twiter: twitter.com/AndreyVeselov
Facebook: www.facebook.com/veselov.andrey
Блог: andrey.moveax.ru
Какие подробности ASP.NET5 вам интересно узнать?

Проголосовало 219 человек. Воздержалось 26 человек.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Комментарии (21)


  1. 1andy
    07.08.2015 11:58
    +2

    Самая свежая документация по ASP.NET5 расположена по адресу docs.asp.net/en/latest.

    Вот это удручает. Уже сколько времени прошло, а большая часть документации на docs.asp.net это:
    This topic hasn’t been written yet!


    1. XaocCPS
      07.08.2015 12:18
      -3

      это наверное потому, что большинство функций могут изменятся и писать документацию сейчас никто не будет. Кроме того, проект открытый и открытый для вклада со стороны, много контента сделано сторонними разработчиками. Сам Microsoft писать подробную документацию к бета-продукту не станет.


      1. smarly_net
        07.08.2015 14:33
        +6

        Если бы это был не официальная страница MS на хабре, я бы прошел бы мимо вашего сообщения. Но раз уж, есть как есть, придется высказаться. Вы все же не шарашкина контора, которая выпускает впервые свою новую библиотеку и не знает что делать, допиливать бету или писать минимальную доку. На каждом углу вы пишите как здорово VS2015, vNext, ASP.NET 5, MVC и так далее, и как бы намекаете, ребята айда все к нам, даже линукс/мак девы, у нас круто. Вы поменяли полностью все в мире ASP.NET и около него, так что все мои знакомые впервые открыв веб проект, могут только F5 нажать (это единственное что им знакомо). Пишите много статей и видео роликов. И в тоже время на «единственную» официальную документацию вы реально забиваете, со словами: «Сам Microsoft писать подробную документацию к бета-продукту не станет.» Так кто же ее будет писать? Вася Пупкин? Который открыл сырцы с гитхаба. Ваши слова выглядят так, как, мы тут все молодцы до безобразия сделали такой продукт, а вы уже сами как хотите разбирайтесь, нам дела нет до какой то там документации, к пуговицам притензии есть? Тогда просто возникает резонный вопрос, а зачем вы уже пол года кричите, что бы все переходили на ваши новые решения, может сначала сделайте до конца, да про доки не забудьте, а после уже пиартесь, коль не можете найти пару человек в команду, которые бы просто следили за изменениями и оперативно вносили их в документация. Да даже в лично ваших статьях я узнал некоторые моменты, которые на гордом docs.asp.net/en/latest значатся как This topic hasn’t been written yet!

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


      1. alemiks
        07.08.2015 19:22

        вы так говорите, как будто на MSDN вообще нет документации на sql server 2016 (который даже в pre-pre-pre-beta)


  1. Shersh
    07.08.2015 12:58

    Асинхронность — это просто

    А что будет, если Delay будет больше чем 100 мс? Например минута или пара? Так чтобы выйти за Timeout со стороны клиента.


    1. XaocCPS
      07.08.2015 13:43

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


      1. Shersh
        07.08.2015 13:51

        Когда мы заранее знаем, что именно эта операция займет много времени, то да, мы можем использовать очередь задач, статус, процесс выполнения.
        Но что если *вдруг* база настолько занята, что ответ от нее летит несколько минут. Клиент все это время будет висеть в ожидании ответа от сервера? Или может быть отдаст код 102?


        1. XaocCPS
          07.08.2015 14:15

          ну тут уже включаются правила бизнес-логики… вреоятно стоит прервать ожидание и вернуть ошибку или другой ответ


        1. Bronx
          16.08.2015 12:29

          Отдайте 202 Accepted и, скажем, заголовок «Location» c URL по которому клиенту приходить проверять статус и получать результат.


  1. svekl
    07.08.2015 14:05

    ASP.NET MVC 6 позволяет использовать и другие папки, т.к. ищет их по базовому классу.

    Разве он ищет их не просто по имени? Мне казалось, что в mvc 6 можно использовать POCO классы в роли контроллеров.


    1. XaocCPS
      07.08.2015 14:14

      можно. не по умолчанию. это тема для отдельной статьи.


      1. svekl
        07.08.2015 14:27

        Не могу с Вами согласиться насчёт не по умолчанию, прямо сейчас создал новый проект, создал там класс

            public class TestController
            {
                public string Test()
                {
                    return "Here we go";
                }
            }
        

        Захожу на http:// localhost:57092/test/test, получаю в ответ «Here we go», видимо всё-таки контроллер теперь ищется только по имени


        1. XaocCPS
          10.08.2015 11:31

          да, но шаблоны по умолчанию содержат «старые» классы с наследованием от Controller


  1. foxmuldercp
    08.08.2015 16:02

    В статье встретил формулировку «не надо нагружать модель лишним кодом».
    Но в рекомендациях к другим MVC Фреймворкам очень часто вижу обратный пункт «все должно быть в модели, метод контроллера должен быть прост и минимален».
    Для себя хочу понять «идеологию» этих двух утверждений, почему используется две таких «полярных» идеологии, хотя мне кажется логичным подход, что модель должна иметь свойства, методы, что максимально ресурсоемкие задачи должны быть вынесены вообще на самый мощный, обычно это sql сервер. а логика работы таки да, должна быть в контроллере.

    Спасибо за статью, судя по ней с mvc5 не очень много поменялось, но все равно нет описания о том как использовать bower, grunt, css процессоры, т.к view и его генерация — это достаточно ресурсоёмкая задача, и минимизировать код того же bootstrap'а для выкусывания лишнего и сборки только нужных компонентов — не простая для новичков задача


    1. lexxpavlov
      08.08.2015 19:03

      >максимально ресурсоемкие задачи должны быть вынесены вообще на самый мощный, обычно это sql сервер
      Тут есть большая потенциальная проблема. Дело в том, что масштабировать SQL-сервер на порядок сложнее, чем масштабировать веб-приложение. Серверов с кодом можно рядом поставить очень просто. А вот поставить рядом с текущим SQL-сервером такой же второй — нетривиальная задача (частично зависит от типа SQL-сервера).

      Я решаю эту дилемму, о которой вы говорите, так. Если модель имеет какой-то код, который относится только к этой самой модели (не нужны данные из других моделей), то код в самой модели. Если какой-то код связан с несколькими моделями, то он должен быть в специальных классах (но не в контроллерах!), так называемых сервисах. А контроллер я делаю «тонкий», в нём пару вызовов сервисов и/или моделей.


      1. foxmuldercp
        08.08.2015 19:10

        Про SQL сервер я говорил как раз в контексте «толстых запросов» к БД. Например, насколько я знаю, один из идиотизмов 1с был в том, что она сначала получает ВСЕ данные с sql сервера на клиент, а потом начинает пытаться это все обработать на мелком тонком клиенте или слабенькой рабочей станции, хотя 80% этих задач можно решить через view/function на SQL сервере гораздо более оптимально и быстрее, чем мучать для этого слабенькие офисные машинки.
        Понятно, что «толстые задачи» не относящиеся к БД, например обработка графических файлов, должна выполняться асинхронно на более другом сервисе фоновых воркеров.


    1. StealthDogg
      10.08.2015 06:58

      > а логика работы таки да, должна быть в контроллере.

      Давайте посмотрим шире и уйдет от элементарных демо-приложений из 1-2 контроллеров. Как правило у вас вы будете создавать n-tier (ну или onion) решение, где у вас будет отдельный бизнес-слой. Веб-приложение вообще в этом случае это просто уровень UI. И чем меньше в нем именно бизнес-логики, тем лучше (для сопровождения и модификации в дальнейшем).

      Я даже склонен рассматривать модели MVC приложения как ViewModel в рамках всего приложения. Т.е. контроллер знает что за данные запросили, знает где их взять (в каких сервисах) и формирует некую модель для данной страницы (ведь не всегда то, что надо отобразить 1 в 1 совпадает с моделями BL, не редко это набор из BL).

      > но все равно нет описания о том как использовать bower, grunt, css процессоры

      Все еще впереди :) Кстати, в последних сборках в проект уже по умолчанию включается gulp, а не bower.


      1. masterL
        12.08.2015 12:58

        «Все еще впереди :) Кстати, в последних сборках в проект уже по умолчанию включается gulp, а не bower.»
        Вы, наверно, хотели сказать не bower, а grunt — потому что gulp и bower, насколько я знаю вещи разного плана — первый таск-менеджер, а второй менеджер пакетов.

        Если будете рассматривать тему фронт-енда, то хотелось бы рассмотреть проблему привязки (binding) задач (grunt и gulp) к конфигурации (debug, release и т.д.) билда проекта — если это вообще на данном этапе решаемо. Надеюсь что можно как-то программно вызывать запуск задач — тогда настройку какие задачи запускать, можно делать в Stаrtup файле. Если же binding задач к событиям, вещь в себе и доступна только из контекстного меню Task Runner Explorer — то это конечно очень грустно — задачи для прод и девелоп среды в любом случае будут отличаться, а каждый раз перед публикацией в ручную запускать прод задачи для сборки — не вариант.


        1. StealthDogg
          12.08.2015 13:30

          Да, верно оговорился — речь была про замену grunt на gulp. Спасибо что заметили.

          Мне не совсем ясно почему всегда должны различаться таски для паблиша и разработки. Но тем не менее кроме биндинга в gulpfile.js есть scripts: prepublish в project.config


          1. masterL
            12.08.2015 20:07

            Да, думаю, такой вариант тоже подойдет, спасибо за наводку.


    1. Simplevolk
      11.08.2015 17:01

      Но в рекомендациях к другим MVC Фреймворкам очень часто вижу обратный пункт «все должно быть в модели, метод контроллера должен быть прост и минимален».


      Возможно, вся логика должна находиться в вспомогательных классах, методы которых и вызывает модель? Поправьте, если ошибся.