My dev environment is ASP.NET Core Razor Page on .NET 8, with EF Core and MS SQL Server in Visual Studio 2022.
I am providing the end user with a file upload form (Create.cshtml) to add images to a Bootstrap carousel.
When the image is uploaded the name of the file is changed to a GUID, and the file is written to the wwwroot/images directory in my Solution. This functionality works correctly.
Corresponding data related to the image is written to the database. This functionality does not work.
Issue: When the code in the Create.cshtml.cs file reaches the if (!ModelState.IsValid) line the ModelState is Not Valid, with the following information being provided in the Watch Window: "FileName, SubKey = FileName, Key = FileName, ValidationState = Invalid", with the Value being null.
Associated Class:
public class CarouselItem
{
public int Id { get; set; }
public required string Description { get; set; }
[DisplayName("File Name (GUID)")]
public required string FileName { get; set; }
[DisplayName("Alt File Name")]
public required string AltFileName { get; set; }
[DisplayName("Display Start Date")]
public required DateOnly DisplayStartDate { get; set; }
[DisplayName("Display Stop Date")]
public required DateOnly DisplayStopDate { get; set; }
}
Create.cshtml.cs file:
public class CreateModel : PageModel
{
private readonly IWebHostEnvironment _webHostEnvironment;
private readonly ApplicationDbContext _context;
public CreateModel(IWebHostEnvironment webHostEnvironment, ApplicationDbContext context)
{
_webHostEnvironment = webHostEnvironment;
_context = context;
}
[BindProperty]
public CarouselItem CarouselItem { get; set; }
[BindProperty]
public IFormFile Image { get; set; }
public void OnGet()
{
}
public async Task<IActionResult> OnPostAsync()
{
if (Image != null)
{
var fileExtension = Path.GetExtension(Image.FileName);
if (fileExtension.ToLower() == ".jpg")
{
var newFileName = $"{Guid.NewGuid()}{fileExtension}";
var filePath = Path.Combine(_webHostEnvironment.WebRootPath, "images", newFileName);
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
await Image.CopyToAsync(fileStream);
}
CarouselItem.FileName = newFileName;
CarouselItem.AltFileName = Image.FileName;
}
else
{
ModelState.AddModelError("Image", "Only .jpg files are allowed.");
}
}
if (string.IsNullOrEmpty(CarouselItem.FileName))
{
ModelState.AddModelError("CarouselItem.FileName", "The File Name field is required.");
}
if (!ModelState.IsValid)
{
return Page();
}
_context.CarouselItems.Add(CarouselItem);
await _context.SaveChangesAsync();
return RedirectToPage("/Admin/Carousel/Index");
}
}
Create.cshtml file:
<h1>Create Carousel Items</h1>
<hr />
<form method="post" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="mb-3">
<label for="Description" class="form-label">Description</label>
<input type="text" class="form-control" id="Description" name="Description" asp-for="CarouselItem.Description" />
<span asp-validation-for="CarouselItem.Description" class="text-danger"></span>
</div>
<div class="mb-3">
<label for="AltFileName" class="form-label">Alt File Name</label>
<input type="text" class="form-control" id="AltFileName" name="AltFileName" asp-for="CarouselItem.AltFileName" />
<span asp-validation-for="CarouselItem.AltFileName" class="text-danger"></span>
</div>
<div class="mb-3">
<label for="DisplayStartDate" class="form-label">Display Start Date</label>
<input type="date" class="form-control" id="DisplayStartDate" name="DisplayStartDate" asp-for="CarouselItem.DisplayStartDate" />
<span asp-validation-for="CarouselItem.DisplayStartDate" class="text-danger"></span>
</div>
<div class="mb-3">
<label for="DisplayStopDate" class="form-label">Display Stop Date</label>
<input type="date" class="form-control" id="DisplayStopDate" name="DisplayStopDate" asp-for="CarouselItem.DisplayStopDate" />
<span asp-validation-for="CarouselItem.DisplayStopDate" class="text-danger"></span>
</div>
<div class="mb-3">
<label for="Image" class="form-label">File</label>
<input type="file" class="form-control" id="Image" name="Image" />
<span asp-validation-for="Image" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Create</button>
</form>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}
The CarouselItem.FileName
value is not provided during the form submission. Initially, the ModelState.IsValid
returns false
.
Based on your logic in the back code, you update the CarouselItem.FileName
value once the file is uploaded; but it will not automatically update the ModelState
.
There is a validation to check whether the CarouselItem.FileName
is null
or an empty string. You can use the validation to remove the model error if the validation is passed.
Meanwhile, you should set/remove the error from FileName
instead of CarouselItem.FileName
.
if (string.IsNullOrEmpty(CarouselItem.FileName))
{
ModelState.AddModelError("FileName", "The File Name field is required.");
}
else
{
ModelState.Remove("FileName");
}