In the last module we added the Create feature in order to let users add Vehicles to the database.
In this module we will add an Update feature so that users can update vehicle information.
A lot of the steps in creating the update/edit feature are going to be similar to the steps we took in developing the create feature in the last module.
The first thing we need to do is again extend the vehicles repo to include a method for updating a vehicle.
Extend the Vehicles Repository
Modify IVehicleRepository.cs with the code shown below.
FredsCars\Models\Repositories\IVehicleRepository.cs
namespace FredsCars.Models.Repositories
{
public interface IVehicleRepository
{
IQueryable<Vehicle> Vehicles { get; }
Task CreateAsync(Vehicle vehicle);
Task UpdateAsync(Vehicle vehicle);
}
}
Next modify EFVehicleRepository.cs with the code shown below.
FredsCars\Models\Repositories\EFVehicleRepository.cs
using FredsCars.Data;
namespace FredsCars.Models.Repositories
{
public class EFVehicleRepository : IVehicleRepository
{
private FredsCarsDbContext _context;
public EFVehicleRepository(FredsCarsDbContext context)
{
_context = context;
}
public IQueryable<Vehicle> Vehicles => _context.Vehicles;
public async Task CreateAsync(Vehicle vehicle)
{
await _context.Vehicles.AddAsync(vehicle);
await _context.SaveChangesAsync();
}
public async Task UpdateAsync(Vehicle vehicle)
{
_context.Vehicles.Update(vehicle);
await _context.SaveChangesAsync();
}
}
}
In this section we simply added a method definition to the Vehicle repo interface called UpdateAsync
which takes in a Vehicle object and returns a Task and implemented it the EFVehicleRepository
class much like we did for the Create feature in the last module. The implimentation uses the Vehicles DbSet’s Update method where the CreateAsync method used the Vehicles DbSet’s AddAsync method. A DbSet only has an Upddate method and no UpdateAsync method. But the change to the Entity’s state will be saved to the database asyncronously through the SaveChangesAsync method.
Add the Edit action methods to the Vehicles controller
HttpGet Edit
First let’s add the HttpGet Edit action method. Modify the Vehicles controller with the following code.
FredsCars\Controllers\VehiclesController.cs
using FredsCars.Models;
using FredsCars.Models.Repositories;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
namespace FredsCars.Controllers
{
public class VehiclesController : Controller
{
private IVehicleRepository _vehicleRepo;
private IVehicleTypeRepository _vehicleTypeRepo;
public VehiclesController(IVehicleRepository vRepo,
IVehicleTypeRepository vtRepo)
{
_vehicleRepo = vRepo;
_vehicleTypeRepo = vtRepo;
}
... existing code ...
public async Task<ViewResult> Edit(int id)
{
Vehicle? vehicle = await _vehicleRepo.Vehicles
.Include(v => v.VehicleType)
.FirstOrDefaultAsync(v => v.Id == id);
if (vehicle == null)
{
ViewBag.NoVehicleMessage =
"Sorry, no vehicle with that id could be found.";
}
var vehicleTypes =
_vehicleTypeRepo.VehicleTypes;
ViewBag.VehicleTypeList = new SelectList(vehicleTypes,
"Id", "Name");
return View(vehicle);
}
}
}
In the code above for the GET Edit action method we start off by fetching a vehicle by its Id using a LINQ query very similar to the Details action method and assign the result to a variable named vehicle of Type Vehicle?
(nullable Vehicle). Except here we do not include the AsNoTracking
method of the Vehicles DbSet because we need EF Core to track the changes made to the entity update.
Vehicle? vehicle = await _vehicleRepo.Vehicles
.Include(v => v.VehicleType)
.FirstOrDefaultAsync(v => v.Id == id);
HttpPost Edit
Next let’s add the post edit action. Modify the Vehicles controller once more with the code shown below.
FredsCars\Controllers\VehiclesController.cs
using FredsCars.Models;
using FredsCars.Models.Repositories;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
namespace FredsCars.Controllers
{
public class VehiclesController : Controller
{
... existing code ...
public async Task<ViewResult> Edit(int id)
{
Vehicle? vehicle = await _vehicleRepo.Vehicles
.Include(v => v.VehicleType)
.FirstOrDefaultAsync(v => v.Id == id);
if (vehicle == null)
{
ViewBag.NoVehicleMessage =
"Sorry, no vehicle with that id could be found.";
}
return View(vehicle);
}
[HttpPost]
[ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int id)
{
var vehicle = await _vehicleRepo.Vehicles
.Include(v => v.VehicleType)
.FirstOrDefaultAsync(v => v.Id == id);
if (await TryUpdateModelAsync<Vehicle>(vehicle!,
"",
v => v.Status, v => v.Year, v => v.Make,
v => v.Model, v => v.Color, v => v.Price,
v => v.Price, v => v.VIN, v => v.ImagePath
))
{
try
{
// throw new DbUpdateException("500 error on vehicle update.");
await _vehicleRepo.UpdateAsync(vehicle!);
return RedirectToAction("Index", "Home");
}
catch (DbUpdateException ex)
{
// Log the exception
//ex.Message, ex.Source, ex.StackTrace
ModelState.AddModelError("",
"There was a problem updating the vehicle."
+ "Please contact your system administrator." );
}
}
return View();
}
}
}
In the new post code above…