'Edit images in wwwroot
I am a programmer at mvc,
My goal is to use the image I have in the database and edit it into another image that does not necessarily exist in my wwwroot but in my computer.
image: 
Explanation of the image: I have an image in the database here and I want to edit it
Click the edit button
Edit image: 
But when I press the save button I get an error:
NullReferenceException: Object reference not set to an instance of an object. PetShop.Client.Services.FileService.File(CreateAnimalViewModel model) in FileService.cs var path = Path.Combine(wwwPath, "Images", model.Photo!.FileName); PetShop.Client.Controllers.AdminController.EditAnimal(CreateAnimalViewModel model) in AdminController.cs await _file.File(model);
Must note that the code of the service does work when I try to add a new image to wwwroot but does not work in edit
My Service:
public class FileService : IFileService
{
private readonly IWebHostEnvironment _environment;
public FileService(IWebHostEnvironment environment)
{
_environment = environment;
}
public async Task<string> File([FromForm] CreateAnimalViewModel model)
{
string wwwPath = _environment.WebRootPath;
var path = Path.Combine(wwwPath, "Images", model.Photo!.FileName);
if (model.Photo.Length > 0)
{
using var stream = new FileStream(path, FileMode.Create);
await model.Photo.CopyToAsync(stream);
}
return model.Animal!.PhotoUrl = model.Photo.FileName;
}
}
public interface IFileService
{
Task<string> File([FromForm] CreateAnimalViewModel model);
}
My ViewModel:
public class CreateAnimalViewModel
{
public Animal? Animal { get; set; }
public IFormFile Photo { get; set; }
}
My Controller:
public async Task<IActionResult> EditAnimal(int id)
{
var animal = await _repo.FindAnimalById(id);
ViewBag.Category = new SelectList(_repository.GetCategoriesTable(), "CategoryId", "Name");
return View(new CreateAnimalViewModel() { Animal = animal});
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditAnimal([FromForm] CreateAnimalViewModel model)
{
ModelState.Clear();
TryValidateModel(model);
await _file.File(model);
if (!ModelState.IsValid)
{
await _repo.EditAnimal(model.Animal!);
return RedirectToAction(nameof(Manager));
}
return View();
}
My View:
@model PetShop.Client.Models.CreateAnimalViewModel
<div >
<form asp-action="EditAnimal" method="post" >
<div asp-validation-summary="ModelOnly"></div><input type="hidden" asp-for="Animal!.AnimalId" id="Space"/>
<dl class="row" >
<dt class = "col-sm-2"><label asp-for="Animal!.Name" id="Space"></label></dt>
<dd class = "col-sm-10"><input asp-for="Animal!.Name"/><span asp-validation-for="Animal!.Name" ></span></dd>
<dt class = "col-sm-2"><label asp-for="Animal!.BirthDate" id="Space"></label></dt>
<dd class = "col-sm-10"><input asp-for="Animal!.BirthDate"/><span asp-validation-for="Animal!.BirthDate"></span></dd>
<dt class = "col-sm-2"><label asp-for="Animal!.Description" id="Space"></label></dt>
<dd class = "col-sm-10"><input asp-for="Animal!.Description"/><span asp-validation-for="Animal!.Description"></span></dd>
<dt class = "col-sm-2"><label asp-for="Animal!.CategoryId" id="Space"></label></dt>
<dd class = "col-sm-10"><select asp-for="Animal!.CategoryId" asp-items="ViewBag.Category"></select><span asp-validation-for="Animal!.CategoryId"></span></dd>
<dt class = "col-sm-2"><label asp-for="Photo"></label></dt>
<dd class = "col-sm-10"><input type="file" asp-for="Photo" accept="image/*"/>
<span asp-validation-for="Photo"></span></dd>
<br/> <br/> <br/>
<input type="submit" value="Save" id="ButtonDesign"/>
</dl>
</form>
<a asp-action="Commands"><input type="submit" value="Back to Admin Page" id="BackPageButton"/></a>
In view I only show the part of the file all the other things are not relevant to the problem
Edit post Repository:
public async Task<int> AddAnimal(Animal animal)
{
_context.Add(animal!);
return await _context.SaveChangesAsync();
}
public async Task<int> EditAnimal(Animal animal)
{
_context.Update(animal);
return await _context.SaveChangesAsync();
}
public DbSet<Category> GetCategories()
{
var category = _context.Categories;
return category;
}
Solution 1:[1]
Let's begin with your mistaken code,
[FromForm] In EditAnimal Post Method:
You are submitting
[FormBody]but here you are receiving[FromForm]this is one of the reason of yournull reference exception. In your case you needn't to use that instead your can directly useclass.
Photo has no handlear in controller
You are sending image file from your view. But Your controler doesn't have any handeler for that. It should have
IFormFiletype to receive image.
So Wrong way:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditAnimal([FromForm] CreateAnimalViewModel model)
{
}
Note:Based on your correct design and architecture this is not correct way.
Correct way:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditAnimal(CreateAnimalViewModel model, IFormFile photo)
{
if (photo == null || photo.Length == 0)
{
return Content("File not selected");
}
var path = Path.Combine(_environment.WebRootPath, "images", photo.FileName);
using (FileStream stream = new FileStream(path, FileMode.Create))
{
await photo.CopyToAsync(stream);
stream.Close();
}
model.Animal!.PhotoUrl = model.Photo!.FileName;
// Find the existing data
var objAnimal = _context.Animals.Where(aId => aId.AnimalId == model.Animal.AnimalId).FirstOrDefault();
if(model != null)
{
//Update the date with new value
objAnimal!.AnimalId = model.Animal.AnimalId;
objAnimal.Name = model.Animal.Name;
objAnimal.Category = model.Animal.Category;
objAnimal.Description = model.Animal.Description;
objAnimal.PhotoUrl = model.Animal.PhotoUrl;
_context.SaveChanges();
}
return RedirectToAction("Edit", new { id = model!.Animal.AnimalId });
}
Edit Method When Loading:
public async Task<IActionResult> Edit(int id)
{
var animal = await _context.Animals.FindAsync(id);
ViewBag.Category = new SelectList(_repository.GetCategoriesTable(), "CategoryId", "Name");
return View(new CreateAnimalViewModel() { Animal = animal, DisplayPhoto = animal!.PhotoUrl });
}
Note:Here you have done another mistake, there is no point of usingCreateAnimalViewModelrather you can directly returnAnimal Model, as you decided to useCreateAnimalViewModelin that case, the way you are bindingAnimal domain classtoCreateAnimalViewModelyou would loosePhotoUrlso here I have introduced another property inView Modelto avoid extra modification which I will be using to load image onview. So the updatedCreateAnimalViewModelwould be:
Updated CreateAnimalViewModel:
public class CreateAnimalViewModel
{
public Animal? Animal { get; set; }
public string? DisplayPhoto { get; set; }
public IFormFile? Photo { get; set; }
}
Note:If you can use singleViewModelfor thisviewin that caseIFormFile? Photo propertyis also not required, you can directly bind theimagein view. But I didn't modify your existing anything.
View For loading Edit Animal:
@model DotNet6MVCWebApp.Models.CreateAnimalViewModel
<div>
<form asp-action="EditAnimal" method="post" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly"></div><input type="hidden" asp-for="Animal!.AnimalId" id="Space" />
<div>
<h4><strong>Animal Details</strong> </h4>
<table class="table table-sm table-bordered table-striped">
<tr>
<th> <label asp-for="Animal!.Name"></label></th>
<td> <input asp-for="Animal!.Name" class="form-control" placeholder="Enter animal name" /><span asp-validation-for="Animal!.Name"></span></td>
</tr>
<tr>
<th> <label asp-for="Animal!.Description"></label></th>
<td> <input asp-for="Animal!.Description" class="form-control" placeholder="Enter animal description" /><span asp-validation-for="Animal!.Description"></span></td>
</tr>
<tr>
<th> <label asp-for="Animal!.Category"></label></th>
<td> <input asp-for="Animal!.Category" class="form-control" placeholder="Enter animal category" /><span asp-validation-for="Animal!.Category"></span></td>
</tr>
<tr>
<th> <label asp-for="Photo"></label></th>
<td>
<img src="~/images/@Model.Animal!.PhotoUrl"
class="rounded-square"
height="50" width="75"
style="border:1px"
asp-append-version="true" accept="image/*" />
<input type="file" name="photo" accept="image/*" />
</td>
</tr>
<tr>
<th> <button type="submit" class="btn btn-primary" style="width:107px" >Update</button></th>
<td> </td>
</tr>
<tr>
<th>@Html.ActionLink("Back To List", "Index", new { /* id=item.PrimaryKey */ }, new { @class = "btn btn-success" })</th>
<td> </td>
</tr>
</table>
</div>
</form>
</div>
Final Output:
Hope it would guided you accordingly to fix your exception.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |

