id="id1543">
Метод для обновления записи Car
использует HTTP-метод PUT
. Он применяет вспомогательный метод для отправки записи Car
в формате JSON и возвращает обновленную запись Car
из тела ответа:
public async Task<Car> UpdateCarAsync(int id, Car entity)
{
var response = await PutAsJson($"{_settings.Uri}{_settings.CarBaseUri}/{id}",
JsonSerializer.Serialize(entity));
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<Car>();
}
Последний добавляемый метод предназначен для выполнения НТТР-метода DELETE
. Шаблон соответствует остальным методам: использование вспомогательного метода и проверка ответа на предмет успешности. Он ничего не возвращает вызывающему коду, поскольку сущность была удалена. Ниже показан код метода:
public async Task DeleteCarAsync(int id, Car entity)
{
var response = await DeleteAsJson($"{_settings.Uri}{_settings.CarBaseUri}/{id}",
JsonSerializer.Serialize(entity));
response.EnsureSuccessStatusCode();
}
Создайте в каталоге ApiWrapper
проекта AutoLot.Service
новый файл класса по имени ServiceConfiguration.cs
. Приведите операторы using
к следующему виду:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Сделайте класс открытым и статическим, после чего добавьте открытый статический расширяющий метод для IServiceCollection
:
namespace AutoLot.Services.ApiWrapper
{
public static class ServiceConfiguration
{
public static IServiceCollection ConfigureApiServiceWrapper(
this IServiceCollection
services, IConfiguration config)
{
return services;
}
}
}
В первой строке расширяющего метода в контейнер DI добавляется ApiServiceSettings
. Во второй строке в контейнер DI добавляется IApiServiceWrapper
и регистрируется класс с помощью фабрики HTTPClient
. Это позволяет внедрять IApiServiceWrapper
в другие классы, а фабрика HTTPClient
будет управлять внедрением и временем существования HTTPClient
:
public static IServiceCollection ConfigureApiServiceWrapper(this IServiceCollection
services, IConfiguration config)
{
services.Configure<ApiServiceSettings>(
config.GetSection(nameof(ApiServiceSettings)));
services.AddHttpClient<IApiServiceWrapper,ApiServiceWrapper>();
return services;
}
Откройте файл Startup.cs
и добавьте следующий оператор using
:
using AutoLot.Services.ApiWrapper;
Перейдите к методу ConfigureServices()
и добавьте в него показанную ниже строку:
services.ConfigureApiServiceWrapper(Configuration);
Построение класса CarsController
Текущая версия CarsController
жестко привязана к хранилищам в библиотеке доступа к данным. Следующая итерация CarsController
для связи с базой данных будет применять оболочку службы. Переименуйте CarsController
в CarsDalController
(включая конструктор) и добавьте в каталог Controllers
новый класс по имени CarsController
. Код этого класса является практически точной копией CarsController
, но они хранятся по отдельности с целью прояснения разницы между использованием хранилищ и службы.
На заметку! При работе с одной и той же базой данных вам редко придется применять вместе уровень доступа к данным и оболочку службы. Здесь показаны оба варианта, чтобы вы смогли решить, какой из них лучше подходит в вашей ситуации.
Приведите операторы using
к следующему виду:
using System.Threading.Tasks;
using AutoLot.Dal.Repos.Interfaces;
using AutoLot.Models.Entities;
using AutoLot.Services.ApiWrapper;
using AutoLot.Services.Logging;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
Далее сделайте класс открытым, унаследуйте его от Controller
и добавьте атрибут Route
. Создайте конструктор, который принимает экземпляры реализаций IAutoLotServiceWrapper
и IAppLogging
, после чего присвойте оба экземпляра переменным уровня класса. Вот начальный код:
namespace AutoLot.Mvc.Controllers
{
[Route("[controller]/[action]")]
public class CarsController : Controller
{
private readonly IApiServiceWrapper _serviceWrapper;
private readonly IAppLogging<CarsController> _logging;
public CarsController(IApiServiceWrapper serviceWrapper,
IAppLogging<CarsController>
logging)
{
_serviceWrapper = serviceWrapper;
_logging = logging;
}
}
Вспомогательный метод GetMakes()
Вспомогательный метод GetMakes()
строит экземпляр SelectList
со всеми записями Make
в базе данных. Он использует Id
в качестве значения и Name
в качестве отображаемого текста:
internal async Task<SelectList> GetMakesAsync()=>
new SelectList(
await _serviceWrapper.GetMakesAsync(),
nameof(Make.Id),
nameof(Make.Name));
Вспомогательный метод GetOneCar()
Вспомогательный метод GetOneCar()
получает одиночную запись Car
:
internal async Task<Car> GetOneCarAsync(int? id)
=> !id.HasValue ? null : await _serviceWrapper.GetCarAsync(id.Value);
Единственное отличие между открытыми методами действий в этом контроллере и аналогичными методами в CarsDalController
связано с доступом к данным, а также с тем,что все методы определены как асинхронные. Поскольку вы уже понимаете, для чего предназначено то или иное действие, ниже приведены остальные методы, изменения в которых выделены полужирным:
[Route("/[controller]")]
[Route("/[controller]/[action]")]
public async Task<IActionResult> Index()
=> View(await _serviceWrapper.GetCarsAsync());
[HttpGet("{makeId}/{makeName}")]
public async Task<IActionResult> ByMake(int makeId, string makeName)
{
ViewBag.MakeName = makeName;
return View(await _serviceWrapper.GetCarsByMakeAsync(makeId));
}
[HttpGet("{id?}")]
public async Task<IActionResult> Details(int? id)
{
if (!id.HasValue)
{
return BadRequest();
}
var car = await GetOneCarAsync(id);
if (car == null)
{
return NotFound();
}
return View(car);
}
[HttpGet]
public async Task<IActionResult> Create()
{
ViewData["MakeId"] = await GetMakesAsync();
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Car car)
{
if (ModelState.IsValid)
{
await _serviceWrapper.AddCarAsync(car);
return RedirectToAction(nameof(Index));
}
ViewData["MakeId"] = await GetMakesAsync();
return View(car);
}
[HttpGet("{id?}")]
public async Task<IActionResult> Edit(int? id)
{
var car = await GetOneCarAsync(id);
if (car == null)
{
return NotFound();
}
ViewData["MakeId"] = await GetMakesAsync();
return View(car);
}
[HttpPost("{id}")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, Car car)
{
if (id != car.Id)
{
return BadRequest();
}
if (ModelState.IsValid)
{
await _serviceWrapper.UpdateCarAsync(id,car);
return RedirectToAction(nameof(Index));
}
ViewData["MakeId"] = await GetMakesAsync();
return View(car);
}