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

Введение

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

Что такое ASP.NET MVC?

ASP.NET MVC — это программное обеспечение с открытым исходным кодом, созданное Microsoft. В двух словах, это своего рода среда веб-разработки, основанная на ASP.NET. Как следует из названия, это фреймворк MVC, и последний позволяет нам создавать динамические веб-приложения.

Недавно Microsoft выпустила ASP.NET Core. Последний также позволяет нам разрабатывать приложения с использованием шаблона MVC, но имеет несколько иной подход. Основные отличия заключаются в том, что ASP.NET Core является открытым исходным кодом и кроссплатформенным. Но мы не собираемся здесь заострять внимание на этом.

Что такое МВК?

Во-первых, давайте освежим память и посмотрим на MVC. MVC или Model-View-Controller — это шаблон, который позволяет разделить приложение на три основных компонента: Model, Вид, Контроллер. Теперь мы можем обсудить вопрос «является ли MVC шаблоном проектирования или архитектурным шаблоном?», но это уже другая тема. Здесь мы должны сохранить следующую информацию:

Модель

Модель представляет уровень бизнес-логики данных. Бизнес-логика должна быть инкапсулирована в модель вместе с любой логикой реализации для сохранения состояния приложения.

Вид

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

Контроллер

Контроллер – это компонент, который обрабатывает и реагирует на ввод и взаимодействие пользователя. Он отображает соответствующий View с данными Model в качестве ответа. Таким образом, он отвечает за выбор типа Model, с которым нужно работать, и какого View для рендеринга.

Создание нашего проекта

Перейдем в Visual Studio и выберем «Новый проект › Visual C# › Веб › Приложение ASP.NET We (.NET Framework)». После того, как мы ввели имя для нашего приложения, мы можем перейти к следующему шагу и выбрать вариант «MVC». Мы можем установить флажок для модульных тестов, если мы хотим создать вторую сборку для модульного тестирования.

Структура проекта

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

Информация о приложении

  • Properties — свойства приложения (тип приложения, объект запуска, информация о сборке)
  • Ссылки — используются для добавления в проект дополнительных библиотек.

Папки приложений

  • App_Data — данные приложения (например, база данных SQL)
  • Контент — статические файлы, такие как файлы CSS, значки или изображения.
  • Контроллеры — Классы контроллеров
  • Шрифты — файлы пользовательских шрифтов
  • Models — Классы моделей
  • Скрипты — файлы JavaScript
  • Представления — файлы HTML для представлений.

Файлы конфигурации

  • Global.asax — содержит код для реагирования на события уровня приложения, вызванные ASP.NET или HttpModules.
  • packages.config — управляется инфраструктурой NuGet и отслеживает установленные пакеты с их соответствующими версиями.
  • Web.config — содержит настройки, применимые к веб-сайту.

Первый забег

Теперь давайте нажмем «F5» или кнопку «воспроизвести» в Visual Studio и подождем несколько секунд. Это запустит IIS Express и запустит наше веб-приложение. Затем Visual Studio запускает браузер и открывает домашнюю страницу приложения. Если все в порядке, наше приложение будет доступно по адресу «localhost:RANDOM_PORT».

Создание контроллера, модели и представления

Пришло время создать наш первый Контроллер. Для этого в Обозревателе решений нам нужно щелкнуть правой кнопкой мыши папки Контроллеры и выбрать «Добавить › Контроллер». Выберите «Контроллер MVC 5 — Пустой» и назовите наш Контроллер «GamesController». Как мы видим, Контроллер был создан в папке «Контроллеры», но также была сгенерирована новая папка с именем «Игры». папку «Представления». Теперь давайте создадим Модель, щелкнув правой кнопкой мыши папку Models и выбрав «Добавить › Класс». Назовем эту Модель «Игра». Давайте добавим нашей Модели несколько свойств.

public class Game
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Файл Models/Game.cs

Теперь давайте создадим представление. Для этого нам нужно щелкнуть правой кнопкой мыши папку «Views/Games» и выбрать «Добавить › View». Давайте назовем это представление «Index» и в разделе «Макет» выберем макет с именем «_Layout.cshtml» в «Просмотры/Общие».

Вернемся к нашему «GamesController» и заполним его следующим образом:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using WebApplication1.Models;
namespace WebApplication1.Controllers
{
    public class GamesController : Controller
    {
        // GET: Games
        public ActionResult Index()
        {
            Game game = new Game() { Name = "Super Mario Bros" };
            return View(game);
        }
    }
}

Файл Controllers/GamesController.cs

Здесь у нас есть только одно действие, «Index()», в котором мы создаем новый объект «Игра» перед передачей его конкретному представлению. метод View() наследуется от базового класса Controller. Он возвращает объект «ViewResult», производный от «ActionResult».

В нашем недавно созданном View поместим следующий код:

model WebApplication1.Models.Game
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>@Model.Name</h2>

Файл Views/Games/Index.cshtml

В верхней части файла мы видим несколько свойств. «@model» в самом верху определяет тип Модели, используемой с этим Представлением. «ViewBag.Title» — это, по сути, заголовок страницы, отображаемый в браузере. «Макет» — это макет для этого представления. В теле View мы можем увидеть что-то вроде «@Model». Каждое представление имеет это свойство, которое дает нам доступ к Модели, которую мы передаем ей в Контроллере.

Теперь, если мы перейдем к «localhost:RANDOM_PORT/games», у нас должно получиться что-то интересное.

Создание маршрутов и параметров действий

Теперь пришло время разобраться с маршрутами и параметрами действий.

Когда в наше приложение поступает запрос, ASP.NET MVC Framework сопоставляет данные запроса со значениями параметров для методов действий. Если Метод действия принимает параметр, платформа ищет параметр с точно таким же именем в данных запроса. Если параметр существует, он будет передан Действию.

Давайте определим новое Action в нашем «GamesController»:

public ActionResult Edit(int id, string stringParameter, int? optionalParameter)
{
    if (String.IsNullOrWhiteSpace(stringParameter))
    {
        stringParameter = "None";
    }
    if (!optionalParameter.HasValue)
    {
        optionalParameter = 1;
    }
    return Content(String.Format("id={0}&stringParameter={1}&optionParameter={2}", id, stringParameter, optionalParameter));
}

Файл Controllers/GamesController.cs изменен

Здесь мы добавляем три параметра к нашему Action. Первый — это ID, второй — строка, а третий — необязательное целое число. Теперь, если мы перейдем к «localhost:RANDOM_PORT/games/edit?id=1&stringParameter=someString&OptionalParameter=42», у нас должно быть что-то на экране. Здесь мы просто используем строку запроса для передачи наших параметров. Но мы можем сделать что-то более элегантное, определив несколько маршрутов.

Маршруты доступны в файле App_Start/RouteConfig.cs. Как мы видим, существует Маршрут по умолчанию, который подходит для большинства сценариев. Однако давайте определим собственный маршрут. Давайте поместим следующий код перед Маршрутом по умолчанию.

routes.MapRoute(
    name: "EditGame",
    url: "games/edit/{id}/{stringParameter}/{optionalParameter}",
    defaults: new { controller = "Games", action = "Edit" }
);

Определение пользовательского маршрута в App_Start/RouteConfig.cs

Если мы перейдем к «localhost:RANDOM_PORT/games/edit/1/foo/42», мы должны что-то увидеть.

Этот способ создания маршрутов не совсем чист, если у нас много пользовательских маршрутов. Итак, давайте удалим этот код и заменим его этим:

routes.MapMvcAttributeRoutes();

Добавление маршрутов атрибутов в App_Start/RouteConfig.cs

Теперь вернитесь к нашему «GamesController», чтобы отредактировать его следующим образом:

[Route("games/edit/{id}/{stringParameter}/{optionalParameter:regex(\\d{2})}")]
public ActionResult Edit(int id, string stringParameter, int? optionalParameter)
{
    ...
}

Файл Controllers/GamesController.cs изменен

Здесь мы просто помещаем шаблон URL в атрибут перед действием «Edit()». Обратите внимание, что в этом случае мы также добавляем ограничение к третьему параметру.

Посмотреть модели

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

Допустим, мы хотим отобразить персонажей, которые есть в нашей игре. Для этого давайте создадим новую Модель с именем «Character».

public class Character
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Файл Models/Character.cs

Теперь давайте создадим папку с именем «ViewModels» в корне нашего проекта. Теперь мы можем поместить новый класс в эту папку. Назовите этот последний «IndexGameViewModel». Мы можем заполнить его так:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using WebApplication1.Models;
namespace WebApplication1.ViewModels
{
    public class IndexGameViewModel
    {
        public Game Game { get; set; }
        public List<Character> Characters { get; set; }
    }
}

Файл ViewModels/IndexGameViewModel.cs

Теперь вернитесь к нашему «GamesController» и отредактируйте его следующим образом:

using System.Web;
using System.Web.Mvc;
using WebApplication1.Models;
using WebApplication1.ViewModels;
namespace WebApplication1.Controllers
{
    public class GamesController : Controller
    {
        // GET: Games
        public ActionResult Index()
        {
            Game game = new Game() { Name = "Super Mario Bros" };
            List<Character> characters = new List<Character>
            {
                new Character { Name = "Mario" },
                new Character { Name = "Luigi" },
                new Character { Name = "Peach" },
                new Character { Name = "Bowser" }
            };
            IndexGameViewModel viewModel = new IndexGameViewModel()
            {
                Game = game,
                Characters = characters
            };
            return View(viewModel);
        }
        ...
    }
}

Файл Controllers/GamesController.cs изменен

Как мы видим, мы добавили несколько строк в действие «Index()». Мы создали «List», содержащий список символов и объект «IndexGameViewModel», который мы передаем в View.

Мы также должны отредактировать наш View следующим образом:

@using WebApplication1.Models;
@model WebApplication1.ViewModels.IndexGameViewModel
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>@Model.Game.Name</h2>
<ul>
    @foreach (Character character in Model.Characters)
    {
        <li>@character.Name</li>
    }
</ul>

Файл Views/Games/Index.cshtml изменен

Мы можем вернуться к «localhost:RANDOM_PORT/games», чтобы увидеть результат.

Использование Entity Framework

Пойдем немного дальше и воспользуемся Entity Framework. Entity Framework — это объектно-реляционный преобразователь (ORM), который позволяет нам работать с базой данных с помощью .NET. объекты. Он поддерживает парадигму разработки под названием Code First. Он позволяет нам создавать объекты Model путем написания простых классов (также известных как классы POCO из «простых объектов CLR»). Мы можем создать базу данных на лету из наших классов.

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

PM> Install-Package EntityFramework -Version 6.2.0

Установка Entity Framework

Теперь давайте создадим еще одну Модель с именем «Enemy» и заполним ее следующим образом:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace WebApplication1.Models
{
    public class Enemy
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

Файл Models/Enemy.cs

Теперь мы можем создать новую папку в корне нашего проекта с именем «DAL» (Уровень доступа к данным). Теперь нам нужно создать «Context». Это класс, который координирует функциональность Entity Framework для данной модели данных.

using System;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
using WebApplication1.Models;
namespace WebApplication1.DAL
{
    public class GameContext : DbContext
    {
        public GameContext() : base("GameContext")
        {
        }
        public DbSet<Enemy> Enemies { get; set; }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
    }
}

Файл DAL/GameContext.cs

Сначала мы передаем имя строки подключения в конструктор.

Затем, поскольку это очень простой пример, мы создаем только одно свойство «DbSet». «DbSet» соответствует таблице базы данных, а сущность соответствует строке в таблице.

Наконец, мы указываем, что не хотим, чтобы наши таблицы в базе данных были во множественном числе.

Теперь мы можем инициализировать базу данных с тестовыми данными. Итак, в папке «DAL» мы можем создать файл с именем «GameInitializer».

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
using WebApplication1.Models;
namespace WebApplication1.DAL
{
    public class GameInitializer : System.Data.Entity.DropCreateDatabaseIfModelChanges<GameContext>
    {
        protected override void Seed(GameContext context)
        {
            var enemies = new List<Enemy>
            {
                new Enemy{Name="Goomba"},
                new Enemy{Name="Koopa Troopa"},
                new Enemy{Name="Lakitu"},
                new Enemy{Name="Boo"}
            };
            enemies.ForEach(e => context.Enemies.Add(e));
            context.SaveChanges();
        }
    }
}

Файл DAL/GameInitializer.cs

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

Метод «Seed()» принимает объект контекста базы данных в качестве входного параметра, а код в методе использует этот объект для добавления новых сущностей в базу данных.

Теперь нам нужно отредактировать файл «Web.config», чтобы использовать наш инициализатор.

<entityFramework>
    <contexts>
        <context type="WebApplication1.DAL.GameContext, WebApplication1">
            <databaseInitializer type="WebApplication1.DAL.GameInitializer, WebApplication1" />
        </context>
    </contexts>
    ...
</entityFramework>

Файл Web.config изменен

Настройка Entity Framework для использования LocalDB

LocalDB — это облегченная версия ядра базы данных SQL Server Express. LocalDB работает в специальном режиме выполнения SQL Server Express, что позволяет нам работать с базами данных как с файлами .mdf. LocalDB по умолчанию устанавливается вместе с Visual Studio.

Давайте снова отредактируем наш файл «Web.config»:

<connectionStrings>
    <add name="GameContext" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=WebApplication1;Integrated Security=SSPI;" providerName="System.Data.SqlClient"/>
</connectionStrings>
<appSettings>
    ...
</appSettings>

Файл Web.config изменен

Другой контроллер

Теперь мы собираемся создать еще один Контроллер с именем «EnemyController», используя параметр «Контроллер MVC 5 с представлениями, используя Entity Framework». Мы можем ввести «Enemy» в качестве класса Model и «GameContext» в качестве контекста базы данных. Через несколько секунд мы видим, что для нас был создан Controller с некоторым кодом и кучей Views.

Пришло время снова запустить наше приложение. Давайте перейдем к «localhost:RANDOM_PORT/enemy», чтобы увидеть результат. Как мы видим, глядя на результат и код, для нас было сделано очень многое.

Миграции

В реальном приложении наша модель данных будет часто меняться. Итак, для каждого изменения нам нужно синхронизировать эту базу данных. На данный момент мы настроили Entity Framework для автоматического удаления и повторного создания базы данных при каждом изменении модели данных. Это нормально для разработки, но не для производства, потому что мы потеряем данные. Чтобы решить эту проблему, нам нужно включить функцию Code First Migrations.

Во-первых, нам нужно отредактировать файл «Web.config»:

...
<connectionStrings>
    <add name="GameContext" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=WebApplication12;Integrated Security=SSPI;" providerName="System.Data.SqlClient"/>
</connectionStrings>
...
<!-- remove or comment <contexts>
    <context type="WebApplication1.DAL.GameContext, WebApplication1">
        <databaseInitializer type="WebApplication1.DAL.GameInitializer, WebApplication1" />
    </context>
</contexts> -->

Файл Web.config изменен

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

Теперь мы можем включить миграцию:

PM> enable-migrations
PM> add-migration InitialCreate

Включение миграции и добавление миграции

Как мы видим, папка «Migrations» создана. В этой папке мы можем найти файл «Configuration», который содержит метод «Seed()», подобный нашему инициализатору. Поместим в этот файл следующий код:

protected override void Seed(WebApplication1.DAL.GameContext context)
{
    var enemies = new List<Enemy>
    {
        new Enemy{Name="Goomba"},
        new Enemy{Name="Koopa Troopa"},
        new Enemy{Name="Lakitu"},
        new Enemy{Name="Boo"}
    };
    enemies.ForEach(e => context.Enemies.AddOrUpdate(p => p.Name, e));
    context.SaveChanges();
}

Файл миграции/Configuration.cs

В папке «Migrations» мы также можем найти код, который создаст базу данных с нуля. Метод Up() создает таблицы базы данных, соответствующие наборам сущностей модели данных, а метод Down() удаляет их.

Теперь отредактируйте нашу Enemy» Model следующим образом:

public class Enemy
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Level { get; set; }
}

Файл Models/Enemy.cs изменен

Затем измените файл «Configuration»:

protected override void Seed(WebApplication1.DAL.GameContext context)
{
    var enemies = new List<Enemy>
    {
        new Enemy{Name="Goomba", Level=1},
        new Enemy{Name="Koopa Troopa", Level=2},
        new Enemy{Name="Lakitu", Level=5},
        new Enemy{Name="Boo", Level=4}
    };
    enemies.ForEach(e => context.Enemies.AddOrUpdate(p => p.Name, e));
    context.SaveChanges();
}

Файл Migrations/Configuration.cs изменен

Теперь мы можем создать миграцию и выполнить ее:

PM> add-migration EnemyModelEdited
PM> update-database -TargetMigration EnemyModelEdited

Создание и выполнение переноса

Мы можем использовать Server Explorer, чтобы увидеть, что наша миграция была выполнена.

Модульное тестирование

Поговорим немного о модульных тестах. Если мы установили соответствующий флажок при создании проекта, мы должны увидеть в Обозревателе решений вторую Сборку, созданную для модульных тестов. Если это не так, мы должны добавить новый проект в наше решение (щелкните правой кнопкой мыши наше решение, «Добавить › Проект»), используя шаблон модульного теста. После этого нам нужно было создать ссылку на наш первый проект во втором проекте (щелкните правой кнопкой мыши «Ссылки», «Добавить ссылку»). Нам также нужно добавить несколько других вещей, таких как «System.Web» в ссылке. Затем нам нужно установить пакет «Microsoft AspNet Mvc». Это, вероятно, потребует от нас обновить пакет в первом проекте.

Не усложняйте задачу и поместите в файл «UnitTest1.cs» следующий код:

using System;
using System.Web;
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WebApplication1;
using WebApplication1.Controllers;
namespace WebApplication1Test
{
    [TestClass]
    public class GameControllerTest
    {
        [TestMethod]
        public void Index()
        {
            GamesController controller = new GamesController();
            ViewResult result = controller.Index() as ViewResult;
            Assert.IsNotNull(result);
        }
    }
}

Файл UnitTest1.cs

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

Вывод

В этой статье мы получили обзор ASP.NET MVC Framework. Мы увидели основные концепции, которые он использует. Мы создали небольшое приложение с использованием шаблона MVC и увидели различные варианты, которые у нас есть для создания динамического веб-приложения. Мы увидели, как создавать базовые модели, представления и контроллеры. У нас был краткий обзор Entity Framework и способов его использования. Теперь мы готовы идти дальше, потому что, конечно же, здесь мы не говорили о производственной среде или шаблоне репозитория.

Последнее слово

Если вам понравилась эта статья, вы можете поддержать и помочь мне на Patreon! Это было бы круто! В противном случае вы можете найти другие мои посты на Medium и Tumblr. Вы также узнаете больше обо мне на моем личном сайте. До следующего раза, счастливой головной боли!