Presentation Layer

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core

Последнее обновление: 20.03.2018

Presentation Layer или уровень представления отвечает за взаимодействие с пользователем. Для этого в нашем решении уже есть проект NLayerApp.WEB. Правда, фактически всю основную работу и весь основной функционал мы создали, осталось только его применить на уровне представления. Для этого вначале добавим в проект ссылку на проект BLL:

Проект Presentation Layer в ASP.NET MVC 5

Также добавим в проект через NuGet пакеты AutoMapper, EntityFramewok и Ninject.MVC5, которые нам потом понадобятся.

Для представления данных в представлениях определим две модели представления в папке Models. Класс PhoneViewModel, представляющий смартфон:

namespace NLayerApp.WEB.Models
{
    public class PhoneViewModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Company { get; set; }
        public decimal Price { get; set; }
    }
}

И класс OrderViewModel, представляющий заказ:

namespace NLayerApp.WEB.Models
{
    public class OrderViewModel
    {
        public int PhoneId { get; set; }
        public string Address { get; set; }
        public string PhoneNumber { get; set; }
    }
}

И тогда контроллер HomeController будет выглядеть следующим образом:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NLayerApp.BLL.Interfaces;
using NLayerApp.BLL.DTO;
using NLayerApp.WEB.Models;
using AutoMapper;
using NLayerApp.BLL.Infrastructure;

namespace NLayerApp.WEB.Controllers
{
    public class HomeController : Controller
    {
        IOrderService orderService;
        public HomeController(IOrderService serv)
        {
            orderService = serv;
        }
        public ActionResult Index()
        {
            IEnumerable<PhoneDTO> phoneDtos = orderService.GetPhones();
			var mapper = new MapperConfiguration(cfg => cfg.CreateMap<PhoneDTO, PhoneViewModel>()).CreateMapper();
			var phones = mapper.Map<IEnumerable<PhoneDTO>, List<PhoneViewModel>>(phoneDtos);
            return View(phones);
        }

        public ActionResult MakeOrder(int? id)
        {
            try
            {
                PhoneDTO phone = orderService.GetPhone(id);
                var order = new OrderViewModel { PhoneId = phone.Id };
                
                return View(order);
            }
            catch (ValidationException ex)
            {
                return Content(ex.Message);
            }
        }
        [HttpPost]
        public ActionResult MakeOrder(OrderViewModel order)
        {
            try
            {
                var orderDto = new OrderDTO { PhoneId = order.PhoneId, Address = order.Address, PhoneNumber = order.PhoneNumber };
                orderService.MakeOrder(orderDto);
                return Content("<h2>Ваш заказ успешно оформлен</h2>");
            }
            catch (ValidationException ex)
            {
                ModelState.AddModelError(ex.Property, ex.Message);
            }
            return View(order);
        }
        protected override void Dispose(bool disposing)
        {
            orderService.Dispose();
            base.Dispose(disposing);
        }
    }
}

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

Чтобы перехватить ошибки валидации, которые возникают на уровне BLL, здесь используется конструкция try..catch, в которой перехватываются исключения ValidationException, а соответствующие сообщения об ошибках передаются в представление.

В папке Views/Home определим для действий этого контроллера представления. Представление Index.cshtml:

@model IEnumerable<NLayerApp.WEB.Models.PhoneViewModel>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Name)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Company)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Price)
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Company)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Price)
        </td>
        <td>
            @Html.ActionLink("Order", "MakeOrder", new { id=item.Id })
        </td>
    </tr>
}

</table>

Представление MakeOrder.cshtml:

@model NLayerApp.WEB.Models.OrderViewModel

@{
    ViewBag.Title = "MakeOrder";
}

<h2>Make Order</h2>


@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    
    <div class="form-horizontal">
        <input type="hidden" name="PhoneId" value="@Model.PhoneId" />

        <div class="form-group">
            @Html.LabelFor(model => model.Address, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Address, new { htmlAttributes = new { @class = "form-control" } })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.PhoneNumber, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.PhoneNumber, new { htmlAttributes = new { @class = "form-control" } })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Make Order" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

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

using Ninject.Modules;
using NLayerApp.BLL.Services;
using NLayerApp.BLL.Interfaces;

namespace NLayerApp.WEB.Util
{
    public class OrderModule : NinjectModule
    {
        public override void Load()
        {
            Bind<IOrderService>().To<OrderService>();
        }
    }
}

В данном случае интерфейс IOrderService сопоставляется с классом OrderService.

Теперь перейдем к файлу Global.asax и изменим его следующим образом:

using Ninject;
using Ninject.Modules;
using Ninject.Web.Mvc;
using NLayerApp.BLL.Infrastructure;
using NLayerApp.WEB.Util;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace NLayerApp.WEB
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            // внедрение зависимостей
            NinjectModule orderModule = new OrderModule();
            NinjectModule serviceModule = new ServiceModule("DefaultConnection");
            var kernel = new StandardKernel(orderModule, serviceModule);
            DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
        }
    }
}

В данном случае мы регистрируем свой сопоставитель зависимостей. Причем будут использоваться сразу два модуля Ninject, которые устанавливают сопоставление зависимостей. Первый модуль - это тот модуль, который создан выше и который сопоставляет зависимость IOrderService с сервисом OrderService. А второй модуль взят из проекта BLL, который сопоставлял IUnitOfWork с EFUnitOfWork и передавал им название подключения. В данном случае устанавливается строка подключения DefaultConnection. Соответственно это подключение должно быть определено в файле web.config. При желании затем мы сможем изменить строку подключения.

Проект уровня представления будет иметь следующую структуру:

Presentation Layer in ASP.NET MVC

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

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850