Top Banner
ASP.NET MVC за пределами Hello World 1 Дятлов Александр
77

ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Mar 03, 2017

Download

Technology

dev2devConf
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

ASP.NET MVC за пределами Hello World

1

Дятлов Александр

Page 2: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

О себеВеб-разработка около 4х лет

ASP.NET MVC более 3х лет

Благодаря ASP.NET влюбился в платформу .NET

Люблю выпить и поговорить про архитектуру :)

2

Page 3: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

О чем доклад?Архитектура MVC

Организация бизнес логики приложения

Повторное использование кода

Немного о модульном тестировании

Обработка исключительных ситуаций

3

Page 4: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

История паттерна MVC

Трюгве Миккель Хейердал Реенскауг

1979

Smalltalk

Xerox

4

Page 5: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Пассивная модель

5

Controller

View Model

Page 6: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Активная модель

6

Controller

View Model

Page 7: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Первые шаги в изученииМануалы

Сайт ASP.NETHabrahabr Tutorials...

Учебники по ASP.NET MVCАдам ФрименДжеффри Палермо

...

7

Page 8: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

8

Проблемы понимания

Page 9: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Последствия

1. Бизнес-логика в контроллерах

9

Page 10: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

public ActionResult GetByTagAndDate( string tagName, DateTime date, string authorName)

{

var posts = _dbContext.Posts.Where(p => p.Tags.Any(t => t.Name == tagName)

&& p.CreateOn < date

&& p.Author.Name == authorName);

if (!posts.Any())

return HttpNotFound();

return View(posts);

}

10

Page 11: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

public ActionResult GetByTagAndDate(string tagName, DateTime date, string authorName) {

...

bool troll = (bool)Session["IsTroll"];

if (troll || _dbContext.Posts.Where(p => p.Author.Name == User.Identity.Name).SelectMany(p => p.Marks).Count(m => m.IsLike == true) > _dbContext.Marks.Count(m => m.Author.Name == User.Identity.Name && m.IsLike

== false))

ViewBag.IsTroll = true;

...

11

И еще немножко бизнес-логики

Page 12: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

12 public class PostsV1Controller : Controller {

13 public ActionResult GetByTagAndDate(string tagName, DateTime date, string authorName) { … }

40 public ActionResult GetPostByTitle(string title) { … }

90 public ActionResult GetAll() { … }

1090 public ActionResult History() { … }

}12

И начинается АД

Page 13: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Последствия1. Бизнес-логика в контроллерах2. Использование динамических объектов для передачи данных

представлению

13

Page 14: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

public ActionResult GetByTagAndDate(string tagName, DateTime date,

string authorName)

{

...

ViewBag.IsTroll = true;

return View(posts);

}

14

Потенциальное падение во время исполнения

Page 15: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Проверка на стороне представления

@{

var troll = false;

if (ViewBag.IsTroll != null)

troll = (bool)ViewBag.IsTroll;

}

@if (troll) {

<p>Ты мерзкий тролль!!!</p>

}

15

Page 16: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Последствия1. Бизнес-логика в контроллерах2. Использование динамической типизации для передачи данных

представлению3. Навешивание на бизнес-сущность атрибутов валидации

16

Page 17: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

public class User_Entity {

[Required]

[StringLength(100)]

[Remote("CheckUserName", "Account")]

public string Name { get; set; }

[Required]

[SecurePassword]

public string Password { get; set; }

}

17

Page 18: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

public class User_Entity {

[Required]

[StringLength(100)]

[Remote("CheckUserName", "Account")]

public string Name { get; set; }

[Required]

[SecurePassword]

public string Password { get; set; }

}

18

Page 19: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Последствия1. Бизнес-логика в контроллерах2. Использование динамической типизации для передачи данных

представлению3. Навешивание на бизнес-сущность атрибутов валидации4. Использование одной модели для отображения и получения

данных

19

Page 20: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

20

User_Entity[Required]

[StringLength(100)]

[Remote("CheckUserName", "Account")]

public string Name { get; set; }

[Required]

[SecurePassword]

public string Password { get; set; }

Список пользователей

Page 21: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

21

User_Entity[Required]

[StringLength(100)]

[Remote("CheckUserName", "Account")]

public string Name { get; set; }

[Required]

[SecurePassword]

public string Password { get; set; }

Список пользователей

Информация о пользователе

Page 22: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

22

User_Entity[Required]

[StringLength(100)]

[Remote("CheckUserName", "Account")]

public string Name { get; set; }

[Required]

[SecurePassword]

public string Password { get; set; }

Список пользователей

Информация о пользователе

Сменить пароль

Page 23: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

23

User_Entity[Required]

[StringLength(100)]

[Remote("CheckUserName", "Account")]

public string Name { get; set; }

[Required]

[SecurePassword]

public string Password { get; set; }

Список пользователей

Информация о пользователе

Сменить пароль

Регистрация

Page 24: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Взаимодействия компонентов

24

Контроллер Модель

Представление

Хранение(обычно в реляционнойбазе данных)

Запрос HTTP

Ответ Модель представления

Page 25: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Взаимодействия компонентов

25

Контроллер Модель

Представление

Хранение(обычно в реляционнойбазе данных)

Запрос HTTP

Ответ Модель представления

Page 26: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Обязанности контроллера

26

Cache

Session

Cookies

Controller

Page 27: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Обязанности контроллера

27

Сервисы

Запросы

...

Команды

User.Identity.Name...

Controller

Page 28: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

28

Тестирование контроллеров

Page 29: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

public ActionResult GetByTagAndDate(string tagName, DateTime date, string authorName)

{

...

bool troll = (bool)Session["IsTroll"];

...

return View(posts);

}

29

Зависимость от сессии

Page 30: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Если необходимость в тестировании контроллеров осталась?

30

Page 31: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

public ActionResult GetByTagAndDate(string tagName, DateTime date, string authorName,

Troll troll)

{

...

if (troll)

...

return View(posts);

}

31

Получение через параметры

Page 32: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

public class TrollModelBinder : IModelBinder {public object BindModel(ControllerContext controllerContext,

ModelBindingContext bindingContext) {var troll =

(Troll)controllerContext.HttpContext.Session["IsTroll"];

if (troll == null) {troll = new Troll();controllerContext.HttpContext.Session["IsTroll"] = troll;

}return troll;

}}

32

Кастомный биндинг

Page 33: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Регистрация

protected void Application_Start()

{

...

ModelBinders.Binders.Add( typeof(Troll), new TrollModelBinder());

BundleTable.Bundles.EnableDefaultBundles();

...

}

33

Page 34: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Взаимодействия компонентов

34

Контроллер Модель

Представление

Хранение(обычно в реляционнойбазе данных)

Запрос HTTP

Ответ Модель представления

Page 35: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Модель / Виды

35

АМ~80%

БМ~20%

Личного опыт

Page 36: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

АМ / БМАнемичная модель (бедная модель)

Сущности представляют данные

Сущности это плоские классы

Богатая модель

Единый язык между разработчиком и специалистом

Модель это дистиллированное знание36

Page 37: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Анемичная модель

37

CRUD_Service<T>

+Create()

+Read()

+Update()

+Delete()

Post

Topic

Tag

...

Page 38: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

38

Единый механизм

Page 39: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

В реальном мире

Интернет-блог

Каталог товаров

Сайты галереи

Сайты портфолио

Сайты визитки

и т.д.

39

Page 40: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

В реальном мире

Интернет-блог

Каталог товаров

Сайты галереи

Сайты портфолио

Сайты визитки

и т.д.

40

Page 41: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Богатая модель / Кредит

41

Сredit_Entity

int DaysCountfloat InterestRatedecimal Sum

decimal Payment()

Page 42: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Богатая модель / Грузоперевозки

42

Shipping_Entity

float CargoTotalfloat CargoCurrentint OverflowRate

bool Add(float cargo)float FreePlace()

Page 43: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

В реальном миреАвтоматизация банковских систем

Автоматизация транспортной логистики

Экспертные системы

и т.д.

43

Page 44: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

В реальном миреАвтоматизация банковских систем

Автоматизация транспортной логистики

Экспертные системы

и т.д.

44

Page 45: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Доступ к данным

45

Page 46: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

DAL / РекомендацииPersistence Ignorance

Сущности не должны зависеть от способа хранения данных

Использование паттерна Repository

46

Page 47: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Repository

47

CRUD_Repository<T>

+Create()

+Read()

+Update()

+Delete()

Post

Topic

...

CRUD_Service<T>

Troll_Detector

+ Detect()

Page 48: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

DAL / Рекомендации

Persistence Ignorance

Сущности не должны зависеть от способа хранения данных

Использование паттерна Repository

Более гибкое решение:

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

48

Page 49: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Взаимодействия компонентов

49

Контроллер Модель

Представление

Хранение(обычно в реляционнойбазе данных)

Запрос HTTP

Ответ Модель представления

Page 50: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

public class PostsGetByTagAndDateViewModel {

public IEnumerable<Post> Posts { get; set; }

public bool IsTroll { get; set; }

public PostsGetByTagAndDateViewModel (IEnumerable<Post> posts, bool isTroll) {

Posts = posts;

IsTroll = isTroll;

}

}50

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

Page 51: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Заполнение моделиpublic ActionResult GetByTagAndDate(string tagName, DateTime date,

string authorName, TrollState trollState)

{

...

if (!posts.Any())

return HttpNotFound();

return View(new PostsGetByTagAndDateViewModel(posts, trollState.IsTroll));

}

51

Page 52: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

public class RegistrationViewModel {

[Required]

[StringLength(100)]

[Remote("CheckUserName", "Account")]

public string Name { get; set; }

[Required]

[StringLength(100)]

[SecurePassword]

public string Password { get; set; }

}

52

Page 53: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Классы помощникиpublic static class EmailHelpers {

public static IHtmlString Email(this HtmlHelper html, string address, string name = null) {

return new HtmlString($"<a href=\"mailto: { address }\"> { name ?? address } </a>");

}

}

<span>@Html.Email("[email protected]")</span>

<span>@Html.Email("[email protected]", "Отправить письмо")</span>

53

Page 54: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Классы помощники

@helper Email(string address, string name = null){

<a href="mailto:@(address)">@(name ?? address) </a>}

<span>@Helpers.Email("[email protected]")</span>

<span>@Helpers.Email("[email protected]", "Отправить письмо")</span>

54

App_Code\Helpers.cshtml

Page 55: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Классы помощники

@helper Email(string address, string name = null){

<a href="mailto:@(address)">@(name ?? address) </a>}

<span>@Helpers.Email("[email protected]")</span>

<span>@Helpers.Email("[email protected]", "Отправить письмо")</span>

55

App_Code\Helpers.cshtml

Page 56: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

JavaScript код в представлениях

56

Page 57: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

@model IEnumerable<Post>@{

Layout = "~/Areas/Admin/Views/Shared/_Layout.cshtml";var ajaxSave = new AjaxOptions {

Url = Url.Action("Edit"),OnSuccess = "ajaxSuccess"

};}

<table class="table-striped table-hover tablesorter table">...</table>

<script type="text/javascript" src="@Url.Content("~/Scripts/table.js")"></script><script type="text/javascript">

$(".tablesorter").tablesorter();$("#TableId").change(function () {

window.location.href = "/Posts/Edit/" + $("#PostId").val(); });

</script>

57

Page 58: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

58

Вынос JS кода из представлений

App.views.Posts.Troll = ( function () {// private scope

return {init: function () {

// public API}

}})(jQuery);

Page 59: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

59

Вызов в представленииPostsTroll.js

App.views.Posts.Troll = (function () {// private scope

return {init: function () {

// public API}

}})(jQuery);

<div> </div>

<script> App.views.Posts.Troll.Init();</script>

Page 60: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Проблема HTTP 1.0 / 1.1Дополнительные расходы на установку соединения для каждого запроса

Конвейерная передача HTTP

Параллельные HTTP соединения в современных браузерах от 4 до 8

60

Page 61: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Проблема HTTP 1.0 / 1.1Дополнительные расходы на установку соединения для каждого запроса

Конвейерная передача HTTP

Параллельные HTTP соединения в современных

браузерах от 4 до 8

61

Page 62: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

МинификацияОбъединение JS в один файл при помощи bundle

62

public class BundleConfig {

public static void RegisterBundles(BundleCollection bundles) {

bundles.Add(new ScriptBundle("~/scripts/jquery").Include("~/Scripts/jquery-{version}.js").Include("~/Scripts/PostsTroll.js"));

}

}

Page 63: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

МинификацияОбъединение JS в один файл при помощи bundle

Использование сторонних сборщиков

gulpgrunt...

63

public class BundleConfig {

public static void RegisterBundles(BundleCollection bundles) {

bundles.Add(new ScriptBundle("~/scripts/jquery").Include("~/Scripts/jquery-{version}.js").Include("~/Scripts/PostsTroll.js"));

}

}

Page 64: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Собрать части в целое

64

Page 65: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Архитектура современного веб-приложения

65

ВебUser Interface

Технические сервисы

Technical Services

Зави

сим

ости

Вспомогательная логикаServices

Бизнес-логика (М)Models

Dom

ain

Page 66: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Что делать если?

66

ВебUser Interface

Технические сервисы

Technical Services

Зави

сим

ости

Боле

е сп

ециф

ичес

кая

логи

ка

Вспомогательная логикаServices

Бизнес-логика (М)Models

Dom

ain

Page 67: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Инверсия управления (IoC)

67

ВебUser Interface

Технические сервисы

Technical Services

IPayService Зави

сим

ости

Боле

е сп

ециф

ичес

кая

логи

ка

Вспомогательная логикаServices

Бизнес-логика (М)Models

Dom

ain

Page 68: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Инверсия управления (IoC)

68

ВебUser Interface

Технические сервисы

Technical Services

IPayService

PayServiceIPayService

Зави

сим

ости

Боле

е сп

ециф

ичес

кая

логи

ка

Вспомогательная логикаServices

Бизнес-логика (М)Models

Dom

ain

Page 69: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Регистрация зависимостей

public class DIConfig : Module {

protected override void Load(ContainerBuilder builder) {

builder.RegisterType< PayService>().As<IPayService>();

base.Load(builder);

}

}

69

Page 70: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Что делать если что-то пошло не так?

70

Page 71: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Иерархия исключений .NET

71

Exception

ApplicationExceptionSystemException

Page 72: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Но не бывает так легко

72

Page 73: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Решение

73

DemoException

ApplicationException

Page 74: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Решение

74

DemoException

LogicExceptionFatalException

ApplicationException

Page 75: ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

Обрабатываем конкретные типы

75

try {

// Логика подтверждения оплаты

}

catch (PaymentNotVerifiedLogicException ex)

{

PaymentVerified = false;

_logger.Warning($"При оплате возникли проблемы { ex }");

}