Структура интернет-магазина

123 Исходный код проекта

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

Наше приложение под названием GameStore будет следовать классическому подходу, который повсеместно используется в интернет-магазинах. Мы создадим онлайновый каталог товаров, который пользователи могут просматривать по категориям и страницам, корзину для покупок, куда пользователи могут добавлять и удалять товары, и форму оплаты, где пользователи могут вводить сведения, связанные с доставкой. Кроме того, мы создадим административную область, которая включает в себя средства создания, чтения, обновления и удаления (create, read, update, delete - CRUD) для управления каталогом товаров, и защитим ее так, чтобы изменения могли вносить только зарегистрированные администраторы.

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

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

Создание решения и проектов Visual Studio

Нам предстоит создать решение Visual Studio с тремя проектами внутри. Один проект будет содержать модель предметной области, второй - приложение MVC, а третий - модульные тесты. Для начала мы создаем новое решение Visual Studio по имени GameStore с использованием шаблона Blank Solution (Пустое решение), который находится в подразделе Visual Studio Solutions (Решения Visual Studio) раздела Other Project Types (Другие типы проектов) в диалоговом окне New Project (Новый проект), как показано на рисунке ниже. Щелкните на кнопке ОК, чтобы создать решение.

Создание нового решения Visual Studio

Решение Visual Studio - это контейнер для одного и более проектов. Для рассматриваемого примера приложения требуются три проекта, которые описаны в таблице ниже. Для добавления проекта необходимо щелкнуть правой кнопкой мыши на элементе Solution (Решение) в окне Solution Explorer (Проводник решения) и выбрать в контекстном меню пункт Add --> New Project (Добавить --> Новый проект).

Три проекта для приложения GameStore
Имя проекта Шаблон проекта Visual Studio Назначение
GameStore.Domain

Class Library (Библиотека классов)

Содержит сущности и логику предметной области; настраивается на обеспечение постоянства посредством хранилища, которое создано с помощью инфраструктуры Entity Framework

GameStore.WebUI

ASP.NET MVC Web Application (Веб-приложение ASP.NET MVC 5); когда будет предложено выбрать шаблон проекта, укажите вариант Empty (Пустое) и отметьте флажок MVC

Содержит контроллеры и представления; выступает в качестве пользовательского интерфейса для приложения GameStore

GameStore.UnitTests

Unit Test Project (Проект модульного тестирования)

Содержит модульные тесты для других двух проектов

В примерах применяется вариант Empty шаблона ASP.NET MVC Web Application. Другие варианты предусматривают добавление в проект начального содержимого, в состав которого входят библиотеки JavaScript, таблицы стилей CSS и классы C#, конфигурирующие такие функциональные возможности приложения, как безопасность и маршрутизация. Ничего из этого по своему существу не является плохим, а некоторые библиотеки с открытым кодом, с разрешения Microsoft благополучно включаемые в новые проекты, просто великолепны, однако все содержимое и конфигурацию можно настроить вручную и в процессе этой настройки узнать больше сведений о работе инфраструктуры MVC Framework.

После того как три проекта созданы, окно Solution Explorer должно выглядеть похожим на показанное на рисунке ниже:

Проекты, отображаемые в окне Solution Explorer

Файл Class1.cs, добавленный Visual Studio в проект GameStore.Domain, был удален, поскольку использоваться он не будет. Для упрощения отладки щелкните правой кнопкой мыши на проекте GameStore.WebUI и выберите в контекстном меню пункт Set as Startup Project (Установить в качестве стартового проекта); имя проекта выделится полужирным. Это означает, что данный проект будет запускаться при выборе пункта Start Debugging (Запустить отладку) или Start without Debugging (Запустить без отладки) в меню Debug (Отладка).

Установка пакетов с инструментами

В нашем проекте будут применяться инструменты Ninject и Moq. Выберите пункт меню Tools --> Library Package Manager --> Package Manager Console, чтобы открыть окно командной строки NuGet. Введите следующие команды:

Install-Package Ninject -version 3.0.1.10 -projectname GameStore.WebUI
Install-Package Ninject.Web.Common -version 3.0.0.7 -projectname GameStore.WebUI
Install-Package Ninject.MVC3 -Version 3.0.0.6 -projectname GameStore.WebUI
Install-Package Ninject -version 3.0.1.10 -projectname GameStore.UnitTests 
Install-Package Ninject.Web.Common -version 3.0.0.7 -projectname GameStore.UnitTests
Install-Package Ninject.MVC3 -Version 3.0.0.6 -projectname GameStore.UnitTests
Install-Package Moq -version 4.1.1309.1617 -projectname GameStore.WebUI
Install-Package Moq -version 4.1.1309.1617 -projectname GameStore.UnitTests
Install-Package Microsoft.Aspnet.Mvc -version 5.0.0 -projectname GameStore.Domain
Install-Package Microsoft.Aspnet.Mvc -version 5.0.0 -projectname GameStore.UnitTests

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

Добавление ссылок между проектами

Мы должны настроить зависимости между проектами, а также от определенных сборок Microsoft. Щелкните правой кнопкой мыши на каждом проекте в окне Solution Explorer, выберите в контекстном меню пункт Add Reference (Добавить ссылку) и в разделах Assemblies --> Framework (Сборки --> Инфраструктура), Assemblies --> Extensions (Сборки --> Расширения) или Solution (Решение) добавьте ссылки, описанные в таблице ниже:

Обязательные зависимости между проектами
Имя проекта Зависимости внутри решения Зависимости от сборок
GameStore.Domain

Отсутствуют

System.ComponentModel.DataAnnotations

GameStore.WebUI

GameStore.Domain

Отсутствуют

GameStore.UnitTests

GameStore.Domain, GameStore.WebUI

System.Web, Microsoft.CSharp

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

Настройка контейнера DI

В статье "ASP.NET MVC 5 - Внедрение зависимостей" было показано, как использовать библиотеку Ninject для создания специального распознавателя зависимостей, который MVC Framework будет применять при создании экземпляров объектов внутри приложения. Здесь мы собираемся повторить этот процесс и начнем с добавления в проект GameStore.WebUI папки Infrastructure помещения в нее файла класса по имени NinjectDependencyResolver.cs. Содержимое нового файла представлено в примере ниже:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Web.Mvc;
using Moq;
using Ninject;

namespace GameStore.WebUI.Infrastructure
{
    public class NinjectDependencyResolver : IDependencyResolver
    {
        private IKernel kernel;

        public NinjectDependencyResolver(IKernel kernelParam)
        {
            kernel = kernelParam;
            AddBindings();
        }

        public object GetService(Type serviceType)
        {
            return kernel.TryGet(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return kernel.GetAll(serviceType);
        }

        private void AddBindings()
        {
            // Здесь размещаются привязки
        }
    }
}

Как вы можете помнить, следующий шаг предусматривает создание в файле App_Start/NinjectWebCommon.cs шлюза между классом NinjectDependencyResolver, добавленным в проект одним из NuGet-пакетов Ninject, и поддержкой внедрения зависимостей в MVC, как показано в примере ниже:

// ...
private static void RegisterServices(IKernel kernel)
{
    System.Web.Mvc.DependencyResolver.SetResolver(new
        GameStore.WebUI.Infrastructure.NinjectDependencyResolver(kernel));
} 

Запуск приложения

Если выбрать пункт Start Debugging (Запустить отладку) в меню Debug (Отладка), откроется страница сообщения об ошибке. Причина в том, что был запрошен URL, связанный с несуществующим контроллером:

Страница сообщения об ошибке

Модель предметной области

Все проекты MVC Framework начинаются с модели предметной области, поскольку вокруг нее вращаются почти все компоненты в приложении MVC Framework. Учитывая тот факт, что строится приложение электронной коммерции, наиболее очевидной сущностью предметной области, которая понадобится, является товар (в нашем случае компьютерная игра). Создайте внутри проекта GameStore.Domain новую папку под названием Entities, а в ней новый файл класса C# по имени Game.cs. Необходимая структура показана на рисунке ниже:

Создание модели

Вы уже знакомы с определением класса Game, т.к. мы намерены иметь дело с тем же самым классом, который рассматривался в предшествующих статьях. Приведите содержимое файла класса Game.cs в соответствие с примером ниже:

namespace GameStore.Domain.Entities
{
    public class Game
    {
        public int GameId { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

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

Создание абстрактного хранилища

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

Создайте внутри проекта GameStore.Domain новую папку верхнего уровня по имени Abstract, а в ней новый файл интерфейса IGameRepository, содержимое которого показано в примере ниже. Новый интерфейс можно добавить, щелкнув правой кнопкой мыши на папке Abstract, выбрав в контекстном меню пункт Add --> New Item (Добавить --> Новый элемент) и указав шаблон Interface:

using System.Collections.Generic;
using GameStore.Domain.Entities;

namespace GameStore.Domain.Abstract
{
    public interface IGameRepository
    {
        IEnumerable<Game> Games { get; }
    }
}

Этот интерфейс использует интерфейс IEnumerable<T>, чтобы позволить вызывающему коду получать последовательность объектов Game, ничего не сообщая о том, как или где хранятся или извлекаются данные. Класс, зависящий от интерфейса IGameRepository, может получать объекты Game, ничего не зная о том, откуда они поступают или каким образом класс реализации будет их доставлять. В этом и состоит суть шаблона хранилища. В процессе разработки приложения мы будем возвращаться к интерфейсу IGameRepository, добавляя в него необходимые средства.

Создание имитированного хранилища

Определив абстрактный интерфейс, можно реализовать механизм хранения и привязать его к базе данных, но сначала необходимо добавить ряд других частей приложения. Мы создадим имитированную реализацию интерфейса IGameRepository, которая будет замещать хранилище данных до тех пор, пока не дойдет до него дело.

Имитированная реализация определяется и привязывается к интерфейсу IGameRepository в методе AddBindings() класса NinjectDependencyResolver внутри проекта GameStore.WebUI:

//...
using GameStore.Domain.Abstract;
using GameStore.Domain.Entities;

// ...

private void AddBindings()
{
    Mock<IGameRepository> mock = new Mock<IGameRepository>();
    mock.Setup(m => m.Games).Returns(new List<Game>
    {
        new Game { Name = "SimCity", Price = 1499 },
        new Game { Name = "TITANFALL", Price=2299 },
        new Game { Name = "Battlefield 4", Price=899.4M }
    });
    kernel.Bind<IGameRepository>().ToConstant(mock.Object);
}

Для этого дополнения пришлось добавить в файл несколько пространств имен, но процесс создания имитированной реализации хранилища применяет те же приемы с Moq, которые были описаны в статье "Использование Moq". Всякий раз, когда ядро Ninject получает запрос реализации интерфейса IGameRepository, оно должно возвращать один и тот же имитированный объект, поэтому для установки соответствующей области действия Ninject используется метод ToConstant(), как показано ниже:

kernel.Bind<IGameRepository>().ToConstant(mock.Object);

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

Отображение списка товаров

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

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

Добавление контроллера

Щелкните правой кнопкой мыши на папке Controllers в проекте GameStore.WebUI и выберите в контекстном меню пункт Add --> Controller. Укажите вариант MVC 5 Controller - Empty (Контроллер MVC 5 - Пустой) в диалоговом окне Add Scaffold (Добавление шаблона) и щелкните на кнопке Add. В диалоговом окне Add Controller (Добавление шаблона) введите для имени контроллера GameController и щелкните на кнопке Add, чтобы создать файл GameController.cs. Приведите содержимое этого файла в соответствие с кодом ниже:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using GameStore.Domain.Abstract;
using GameStore.Domain.Entities;

namespace GameStore.WebUI.Controllers
{
    public class GameController : Controller
    {
        private IGameRepository repository;
        public GameController(IGameRepository repo)
        {
            repository = repo;
        }
	}
}

Помимо удаления метода действия Index() мы добавили конструктор, который объявляет зависимость от интерфейса IGameRepository. Это приводит к внедрению библиотекой Ninject данной зависимости для хранилища игр при создании экземпляра класса контроллера. Вдобавок также импортировано пространство имен GameStore.Domain, поэтому на классы хранилища и модели можно ссылаться, не указывая их полностью определенные имена.

Наконец, нужно добавить метод действия по имени List(), который будет визуализировать представление, отображающее полный список товаров, как показано в примере ниже:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using GameStore.Domain.Abstract;
using GameStore.Domain.Entities;

namespace GameStore.WebUI.Controllers
{
    public class GameController : Controller
    {
        private IGameRepository repository;
        public GameController(IGameRepository repo)
        {
            repository = repo;
        }

        public ViewResult List()
        {
            return View(repository.Games);
        }
	}
}

Вызов метода View() подобного рода (без указания имени представления) сообщает инфраструктуре о том, что нужно визуализировать стандартное представление для метода действия. Передавая методу View() список объектов Game, мы снабжаем инфраструктуру данными, которыми необходимо заполнить объект Model в строго типизированном представлении.

Добавление компоновки, файла запуска представления и самого представления

Теперь необходимо добавить стандартное представление для метода действия List(). Щелкните правой кнопкой мыши на методе List() в классе GameController и выберите в контекстном меню пункт Add View (Добавить представление).

Введите в поле View Name (Имя представления) имя List, установите в списке Template (Шаблон) вариант Empty (Пустой) и выберите в списке Model Class (Класс модели) класс Game, как показано на рисунке ниже. Удостоверьтесь, что флажок Use A Layout Page (Использовать страницу компоновки) отмечен, и щелкните на кнопке Add, чтобы создать представление:

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

После щелчка на кнопке Add среда Visual Studio создаст не только файл List.cshtml, но также и файлы _ViewStart.cshtml и Shared/_Layout.cshtml. Это удобная возможность, однако, согласно подходу Microsoft к стандартному содержимому, в файл _Layout.cshtml включено содержимое шаблона, которое нам не требуется или нежелательно. Приведите компоновку к виду, показанному в примере ниже:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title</title>
</head>
<body>
    <div>
        @RenderBody()
    </div>
</body>
</html>

Визуализация данных представления

Хотя в качестве типа модели для этого представления был выбран класс Game, в действительности необходимо работать с типом IEnumerable<Game>, поскольку именно такие данные контроллер Game получает из хранилища и передает представлению. В примере ниже отредактировано выражение @model и добавлена HTML-разметка и несколько выражений Razor для отображения подробной информации о товарах в представление List.cshtml:

@using GameStore.Domain.Entities
@model IEnumerable<Game>

@{
    ViewBag.Title = "Товары";
}

@foreach (var p in @Model)
{
    <div>
        <h3>@p.Name</h3>
        <p>@p.Description</p>
        <h4>@p.Price.ToString("# руб")</h4>
    </div>
}

Заголовок страницы также был изменен. Обратите внимание, что для отображения данных представления Razor-выражение @: не требуется. Это связано с тем, что каждая строка содержимого в теле кода либо является директивой Razor, либо начинается с HTML-элемента.

Свойство Price преобразуется в строку с помощью метода ToString("# руб"), который визуализирует числовые значения в виде денежных значений в рублях.

Установка стандартного маршрута

Инфраструктуре MVC Framework необходимо указать, что она должна отправлять запросы, поступающие для корневого URL приложения (http://mysite/), методу действия List() класса GameController. Это делается путем редактирования оператора внутри метода RegisterRoutes() в файле App_Start/RouteConfig.cs, как показано в примере ниже:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace GameStore.WebUI
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Game", action = "List", id = UrlParameter.Optional }
            );
        }
    }
}

В примере были добавлены изменения - Home на Game и Index на List. Более подробно средство маршрутизации ASP.NET рассматривается в разделе по ASP.NET MVC 5. Пока достаточно знать, что это изменение приводит к направлению запросов, поступающих к стандартному URL, методу действия List() из контроллера Game.

Обратите внимание, что в примере в качестве значения controller указано Game, а не GameController, которое является именем класса. Это часть схемы именования ASP.NET MVC, в соответствии с которой классы контроллеров всегда заканчиваются словом Controller, и при ссылке на класс эта часть имени опускается.

Запуск приложения

Итак, все основные компоненты готовы. Мы располагаем контроллером с методом действия, который инфраструктура MVC Framework вызывает, когда запрашивается стандартный URL. Этот метод действия полагается на имитированную реализацию интерфейса хранилища, генерирующую набор простых тестовых данных. Контроллер передает тестовые данные представлению, которое связано с методом действия, а представление отображает простой список со сведениями о каждом товаре.

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

Основная функциональность приложения интернет-магазина

Если такой результат не получен, удостоверьтесь, что вы переходите на корневой URL, а не на URL, связанный с другим действием.

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

Упрощение отладки

Когда вы запускаете проект через меню Debug (Отладка), среда Visual Studio открывает новое окно браузера для отображения приложения, что может занять несколько секунд. Ускорить этот процесс можно с помощью нескольких трюков. Если вы редактируете файлы представлений, но не файлы классов, то можете вносить изменения в Visual Studio при выполняющемся отладчике. Чтобы увидеть эффект от изменений, просто перезагрузите окно браузера. Платформа ASP.NET перекомпилирует представления, построив классы, и отобразит изменения немедленно.

Среда Visual Studio не позволит редактировать файлы классов, когда отладчик функционирует, или вносить какие-то изменения в проект внутри окна Solution Explorer, поэтому описанный прием наиболее удобен во время точной подстройки и доводки HTML-разметки, генерируемой приложением.

Версия Visual Studio 2013 включает новое средство, называемое ссылкой на браузер, которое позволяет держать открытыми несколько окон браузера и перезагружать их из панели меню Visual Studio.

В качестве последней альтернативы можно иметь открытое приложение в отдельном окне браузера. Чтобы сделать это (предполагая, что отладчик уже запускался хотя бы раз), щелкните правой кнопкой мыши на значке IIS Express в области уведомлений системы и выберите в контекстном меню URL вашего приложения. После внесения изменений скомпилируйте решение в Visual Studio, нажав <F6> (заметьте, построение решения, а не отладка на <F5>) или выбрав пункт меню Builds --> Build Solution (Построение --> Построить решение), а затем перейдите в окно браузера и перезагрузите веб-страницу.

Подготовка базы данных

Мы уже можем отображать простые представления, содержащие сведения о товарах, но по-прежнему выводим тестовые данные, возвращаемые имитированной реализацией интерфейса IGameRepository. Прежде чем можно будет реализовать реальное хранилище, понадобится настроить базу данных и заполнить ее некоторыми данными.

Для базы данных будет применяться SQL Server, а доступ к ней будет осуществляться с помощью Entity Framework (EF) - инфраструктуры ORM для платформы .NET. Инфраструктура ORM представляет таблицы, столбцы и строки реляционной базы данных с помощью обычных объектов C#. Следует упомянуть, что LINQ может работать с различными источниками данных, и один из них - Entity Framework. Чуть позже вы увидите, насколько это упрощает решение задачи.

Это область, где доступен широкий диапазон инструментальных средств и технологий. Можно взаимодействовать не только с разными реляционными базами данных, но также с хранилищами объектов, хранилищами документов и некоторыми вообще экзотическими альтернативами. Кроме того, существуют другие инфраструктуры ORM для .NET, каждая из которых использует слегка отличающийся подход - подобное разнообразие позволяет выбрать то, что наиболее подходит для конкретных проектов.

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

Создание базы данных

В примере интернет-магазина на ASP.NET Web Forms 4.5 мы создавали базу данных GameStore. Повторите шаги в указанной статье и для текущего приложения, чтобы создать и наполнить базу. Также в исходниках вы можете найти mdf-файл для этой базы данных, в папке App_Data.

Создание контекста Entity Framework

Последние версии Entity Framework включают замечательное средство, которое называется сначала код (code-first). Идея состоит в том, что в модели можно определить классы, а затем на их основе сгенерировать базу данных.

Это очень удобно для проектов, создаваемых с нуля, но представляет лишь малую часть предлагаемых возможностей. Вместо этого мы намерены продемонстрировать разновидность подхода "сначала код", при котором классы модели связываются с существующей базой данных. Выберите пункт меню Tools --> Library Package Manager --> Package Manager Console, чтобы открыть окно командной строки NuGet. Введите следующие команды:

Install-Package EntityFramework -projectname GameStore.Domain
Install-Package EntityFramework -projectname GameStore.WebUI

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

Эти команды добавляют пакет Entity Framework в решение. Один и тот же пакет должен быть добавлен в проекты Domain и WebUI, что позволит создать классы, которые будут получать доступ к базе данных в указанных проектах.

Следующий шаг заключается в создании класса контекста, который будет ассоциировать модель с базой данных. Создайте в проекте GameStore.Domain новую папку под названием Concrete и поместите в нее новый файл класса по имени EFDbContext.cs. Приведите содержимое этого файла в соответствие с кодом ниже:

using GameStore.Domain.Entities;
using System.Data.Entity;

namespace GameStore.Domain.Concrete
{
    public class EFDbContext : DbContext
    {
        public DbSet<Game> Games { get; set; }
    }
}

Чтобы воспользоваться преимуществом подхода "сначала код", потребуется создать класс, производный от System.Data.Entity.DbContext. Этот класс затем автоматически определяет свойство для каждой таблицы базы данных, с которой необходимо работать.

Имя свойства указывает таблицу, а параметр типа результата DbSet - тип модели, который инфраструктура Entity Framework должна использовать для представления строк в этой таблице. В рассматриваемом случае именем свойства является Games, а параметром типа - Game, т.е. для представления строк в таблице Games инфраструктура Entity Framework будет применять тип модели Game.

Далее Entity Framework нужно указать, как подключаться к базе данных, и это делается путем добавления в файл Web.config проекта GameStore.WebUI строки подключения к базе данных с таким же именем, как у класса контекста:

<configuration>
  <connectionStrings>
    <add name="EFDbContext" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=GameStore;Integrated Security=True" 
         providerName="System.Data.SqlClient"/>
  </connectionStrings>
  <!-- ... -->
</configuration>

Обратите внимание, что здесь мы переключились на другой проект. Логика модели и хранилища определяется в проекте GameStore.Domain, но информация подключения к базе данных помещается в файл Web.config проекта GameStore.WebUI.

В разделе connectionsStrings файла Web.config будет присутствовать еще один элемент add. Среда Visual Studio создает этот элемент по умолчанию, и вы можете его проигнорировать либо удалить, как было сделано выше.

Создание хранилища для объектов Game

Все, что осталось сделать - добавить в папку Concrete проекта GameStore.Domain файл класса по имени EFGameRepository.cs. Приведите содержимое этого файла класса в соответствие с кодом ниже:

using System.Collections.Generic;
using GameStore.Domain.Entities;
using GameStore.Domain.Abstract;

namespace GameStore.Domain.Concrete
{
    public class EFGameRepository : IGameRepository
    {
        EFDbContext context = new EFDbContext();

        public IEnumerable<Game> Games
        {
            get { return context.Games; }
        }
    }
}

Класс EFGameRepository представляет необходимое хранилище. Он реализует интерфейс IGameRepository и использует экземпляр EFDbContext для извлечения данных из базы посредством Entity Framework. По мере добавления к хранилищу функциональных возможностей вы увидите, как выполняется работа с инфраструктурой Entity Framework (и оцените, насколько она проста).

Для применения нового класса хранилища понадобится отредактировать привязки Ninject и заменить имитированное хранилище привязкой к реальному хранилищу. Измените содержимое файла класса NinjectDependencyResolver.cs из проекта GameStore.WebUI, чтобы метод AddBindings() выглядел, как показано в примере ниже:

// ...
using GameStore.Domain.Concrete;

namespace GameStore.WebUI.Infrastructure
{
    public class NinjectDependencyResolver : IDependencyResolver
    {
        // ...

        private void AddBindings()
        {
            kernel.Bind<IGameRepository>().To<EFGameRepository>();
        }
    }
}

Новая привязка указывает Ninject о том, что для обслуживания запросов к интерфейсу IGameRepository необходимо создавать экземпляры класса EFGameRepository. Теперь осталось лишь снова запустить приложение. Результаты его выполнения показаны на рисунке ниже, и теперь видно, что приложение извлекает сведения о товарах из базы данных, а не имитированного хранилища:

Результат реализации реального хранилища

Такой подход с применением Entity Framework для представления базы данных SQL Server в виде последовательности объектов моделей отличается простотой и легкостью, позволяя сосредоточить все внимание на инфраструктуре MVC Framework. Однако при этом, разумеется, мы пропускаем множество деталей функционирования Entity Framework и большое количество разных конфигурационных параметров.

Лучший чат для C# программистов