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

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

Embed Size (px)

Citation preview

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 }");

}