0

I have tried consistently to upload a image in asp.net core yet no matter how I do it I run into the same problem. ModelState.IsValid returns false. I eventually caved and just copied and pasted someone else's work in the hope that if I could just get it to work, even if it was someone else's, I could understand where I was going wrong. Even with my script kiddie technique, my plans were foiled. So now I have come to people who are smarter than I'll ever be.

This is the tutorial that I copied from

My model looks like this:

using Microsoft.EntityFrameworkCore.Metadata.Internal;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

public class ImageModel
{
    [Key]
    public int ImageId { get; set; }

    [Column(TypeName = "nvarchar(50)")]
    public string Title { get; set; }

    [Column(TypeName = "nvarchar(100)")]
    [DisplayName("Image Name")]
    public string ImageName { get; set; }

    [NotMapped]
    [DisplayName("Upload File")]
    public IFormFile ImageFile { get; set; }
}

The relevant part of my controller

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ImageId,Title,ImageFile")] ImageModel imageModel)
{
    var errors = ModelState.Values.SelectMany(v => v.Errors);

    if (ModelState.IsValid)
    {
        // Save image to wwwroot/image
        string wwwRootPath = _hostEnvironment.WebRootPath;
        string fileName = Path.GetFileNameWithoutExtension(imageModel.ImageFile.FileName);
        string extension = Path.GetExtension(imageModel.ImageFile.FileName);
        imageModel.ImageName = fileName = fileName + DateTime.Now.ToString("yymmssfff") + extension;
        string path = Path.Combine(wwwRootPath + "/Image/", fileName);

        using (var fileStream = new FileStream(path, FileMode.Create))
        {
            await imageModel.ImageFile.CopyToAsync(fileStream);
        }

        // Insert record
        _context.Add(imageModel);
        await _context.SaveChangesAsync();

        return RedirectToAction(nameof(Index));
    }

    return View(imageModel);
}

And finally the view

@model ImageModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>ImageModel</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create" enctype="multipart/form-data">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ImageFile" class="control-label"></label>
                <input asp-for="ImageFile" accept="image/*" />
                <span asp-validation-for="ImageFile" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{
        await Html.RenderPartialAsync("_ValidationScriptsPartial");
    }
}

I have taken a look at this question and followed the suggestion. The error that it returns is

ModelError,ModelError.Exception,ModelError.ErrorMessage Microsoft.AspNetCore.Mvc.ModelBinding.ModelError
The Image Name field is required

Thank you for your time

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459

2 Answers2

1

The issue was caused by Nullable reference types check your csproj file:

 <PropertyGroup>
    .......
      <Nullable>enable</Nullable>
    ......
  </PropertyGroup>

If contains the codes below, try to set it as disable

There're mutipule different solutions

solution1: set as below in your startup/program.cs :

builder.Services.AddControllersWithViews(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

For more details,you could check this document

solution2:

Add the [ValidateNever] Attribute on the property you don't want to validate

solution3:

Create a ViewModel doesn't contain ImageName property for the View

Ruikai Feng
  • 6,823
  • 1
  • 2
  • 11
0

ModelState doesn't look at properties in [Bind] instead looks at all property of model.

Create a new view model without the ImageName property or set the ImageName default value

public class ImagePostModel
{
    [Key]
    public int ImageId { get; set; }

    [Column(TypeName = "nvarchar(50)")]
    public string Title { get; set; }

    //[Column(TypeName = "nvarchar(100)")]
    //[DisplayName("Image Name")]
    //public string ImageName { get; set; } = String.Empty Alternative solution

    [NotMapped]
    [DisplayName("Upload File")]
    public IFormFile ImageFile { get; set; }
}
Okan Karadag
  • 2,542
  • 1
  • 11
  • 23