Предыдущий ролик Следующий ролик  

Видео урок: Solution: Implementing transfers

Основы ASP.NET MVC 5

В этом видео я продемонстрирую вам возможное решение предыдущего задания. Поскольку в этом решении используются уже знакомые вам группы компонентов, я не собираюсь заставлять вас наблюдать за тем, как я ввожу весь этот код и разметку. Вместо этого я просто покажу и объясню вам то, что я уже реализовал. Этот код находится в папке Exercise files -> Chapter 9 -> Final. Сначала в TransactionController я добавил два действия Transfer. Первое действие принимает в качестве параметра checkingAccountId и просто отображает форму, с помощью которой пользователь инициирует перевод.

Второе действие обрабатывает отправку формы и первое, что оно делает, - ищет текущий счет данного пользователя, чтобы проверить его баланс. Если баланс счета меньше запрашиваемой суммы перевода, тогда в ModelState добавляется ошибка, подобно тому, как это делалось в действии Withdrawal. Затем ищем счет, на который будет зачисляться перевод. Здесь я использую метод FirstOrDefault, поэтому в случае невалидности данного счета будет возвращаться null, а не выдаваться исключение.

А если этот метод возвращает null, то мы добавляем ошибку с соответствующим сообщением и связываем ее со свойством DestinationCheckingAccountNumber. На данном этапе, если состояние модели валидно, мы добавляем дебетовую транзакцию для текущего пользователя, используя сумму, противоположную запрашиваемой сумме перевода. Затем добавим кредитовую транзакцию для счета, на который зачисляется перевод, используя указанную сумму перевода. В конце мы вызываем метод SaveChanges и даем CheckingAccountService возможность обновить баланс двух счетов.

Итак, если состояние модели валидно, мы возвращаем частичное представление под названием "_TransferSuccess", в которое передаем нашу модель Transfer, являющуюся в данном случае лишь значением параметра этого метода. И если перевод выполнился успешно, то это говорит о том, что мы передали правильную сумму на правильный счет с помощью этих свойств модели. В противном случае, если состояние модели не валидно, мы просто еще раз воспроизводим форму перевода, в которой будут отображаться ошибки. Итак, если вас интересует, почему вы должны передавать эту модель в частичное представление _TransferSuccess, а не в частичное представление _TransferForm, чтобы оно могло отобразить ошибки, то я объясню вам, для чего это делается. В _TransferForm используются вспомогательные методы HTML, которые могут найти все, что им нужно, в объекте ModelState, а в _TransferSuccess из-за того, что мы напрямую обращаемся к свойствам модели, нам нужно явно передавать эту модель. Давайте на минутку вернемся к частичному представлению _TransferForm. Это частичное представление, потому что мы собираемся выполнять обновление формы с помощью Ajax и при необходимости полностью заменять это представление, а также все сообщения об ошибках, которые мы получаем в случае отказа в переводе. Почти все это вы уже видели раньше. Вот анимированное GIF-изображение для отображения прогресса.

Однако кнопка Submit относится к классу Cancel. Это позволяет отключить jQuery валидацию при отправке формы, поскольку мы будем вызывать ее вручную. И только в случае ее вызова мы будем отправлять форму с помощью Ajax. Я не придумывал сам функциональность класса Cancel. Она встроена в библиотеку jQuery validate. В самом верху находится директива model, задающая модель представления TransferViewModel. Давайте быстренько ее рассмотрим. У нас есть Amount и checkingAccountId, два свойства Transaction, которые будут храниться в базе данных.

И еще одно свойство, которое мы используем для построения и валидации формы, а также для создания второй транзакции, но это свойство не будет напрямую сохраняться в базе данных. Это свойство - DestinationCheckingAccountNumber. Наконец, в представлении Transfer мы отображаем частичное представление и задаем скрипт, который будет обрабатывать отправку формы. Я не стал напрямую применять метод Submit к объекту form и вместо этого привязал событие Submit к document с помощью метода On, потому что если при отправке этой формы с помощью Ajax нам нужно будет ее обновить, чтобы показать ошибки, то в этом случае элемент transferForm будет полностью заменен новым элементом form, для которого не будет определено ни одного обработчика событий.

Поэтому вместо того чтобы привязывать этот метод к элементу form, а затем отвязывать его после замены этого элемента, мы делегируем это событие самому элементу document и говорим, что он должен исполнять этот обработчик для событий отправки формы, инициированных элементом с идентификатором transferForm. Внутри мы задаем переменную под названием form, которая является jQuery-оберткой для this, поэтому нам не нужно беспокоиться о его области применения. Затем мы запускаем jQuery валидацию, вызывая метод Valid. Если этот метод возвращает "true", то мы показываем GIF-изображение progress и отправляем на текущий URL.

В качестве параметра data мы можем использовать совокупность элементов form, собранных с помощью метода Serialize. А затем, если все завершается успешно, мы просто заменяем весь элемент form на полученный ответ. А если после done вы добавили метод fail, то вы сознательнее меня. Но если, к примеру, возникает HTTP-ошибка, то скорее всего, мы ничем не сможем помочь пользователю, кроме как сказать: "Sorry, try again" (Повторите позже). Но лучше вообще ничего не делать. Итак, попробуем протестировать наше приложение.

Для начала попробуем перевести, например, миллион долларов на не существующий счет. В результате получаем два сообщения: "Недостаточно средств на счете" и "Неправильно указан счет получателя". Затем проверим свой баланс и попытаемся перевести всю имеющуюся на счете сумму на тот счет, который мы создали в самом начале курса. Для начала удалим этот символ пробела в конце, знак доллара и запятую, в противном случае нам будет сказано, что такого десятичного числа не существует.

Номер первого счета начинался с четырех нулей, а затем шли цифры 123456. Итак, можно попробовать выполнить такой перевод, а затем заглянуть в базу данных или в представление List для дальнейшей проверки. Скажем еще кое-что о наших действиях Transfer. Многие разработчики скажут вам, например, что при таком подходе в наш контроллер добавляется чересчур много логики, а в частности, контроллер не должен отвечать за обработку логики пользовательских ошибок.

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

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