How to fix "Required" annotation not working in .NET - c#

I'm following this tutorial: https://learn.microsoft.com/en-us/aspnet/core/tutorials/razor-pages/validation?view=aspnetcore-2.2
Here's my movie model class:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public class Movie
{
public int ID { get; set; }
[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
[RegularExpression(#"^[A-Z]+[a-zA-Z""'\s-]*$")]
[Required]
[StringLength(30)]
public string Genre { get; set; }
}
But when I test it on my local, these two fields: Title and Genre don't seem to have the validation that I was expecting, see screenshot below:
Here's my controller form of actions:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using RazorPagesMovie.Models;
namespace RazorPagesMovie.Pages.Movies
{
public class CreateModel : PageModel
{
private readonly RazorPagesMovie.Models.RazorPagesMovieContext _context;
public CreateModel(RazorPagesMovie.Models.RazorPagesMovieContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Movie Movie { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movie.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
}
Here's my Create.cshtml:
#page
#model RazorPagesMovie.Pages.Movies.CreateModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Movie</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Movie.Title" class="control-label"></label>
<input asp-for="Movie.Title" class="form-control" />
<span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Movie.ReleaseDate" class="control-label"></label>
<input asp-for="Movie.ReleaseDate" class="form-control" />
<span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Movie.Genre" class="control-label"></label>
<input asp-for="Movie.Genre" class="form-control" />
<span asp-validation-for="Movie.Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Movie.Price" class="control-label"></label>
<input asp-for="Movie.Price" class="form-control" />
<span asp-validation-for="Movie.Price" 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-page="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Here's my folder structure:
At first I thought there might be a sequence of validation, but actually after I filled out Release Date and Price, I was able to create a movie even without filling in Title and Genre. So apparently the validation for these two fields didn't work.
Could anyone share any insight please?
Thanks!

your required field is never null it is empty string and you must prevent your field from being passed by empty strings like this :
[StringLength(60, MinimumLength = 3)]
[Required(AllowEmptyStrings =false)]
public string Title { get; set; }

For required field validation decorate your function with [BindRequired(ErrorMessage = "Title Required")] instead of using [Required(ErrorMessage="Title Required")]

Related

displaying the input box for a different model

I have following two model classes:
public partial class EmployeeInfo
{
public string LastName { get; set; } = null!;
public string FirstName { get; set; } = null!;
public virtual ICollection<EmergencyInfo> EmergencyInfos { get; } = new List<EmergencyInfo>();
}
public partial class EmergencyInfo
{
public string emailAddress { get; set; } = null!;
public string PhoneNumber { get; set; } = null!;
public virtual EmployeeInfo EmployeeInfo { get; set; } = null!;
}
My Razor view to create a new employee looks like this:
#model AckPackage.Models.EmployeeInfo
#{
ViewData["Title"] = "Create";
}
<div class="form-group row">
<div class="col-sm-4">
<label asp-for="LastName" class="control-label"></label>
<input asp-for="LastName" class="form-control input-lg" />
<span asp-validation-for="LastName" class="text-danger"></span>
</div>
<div class="col-sm-4">
<label asp-for="FirstName" class="control-label"></label>
<input asp-for="FirstName" class="form-control input-lg" />
<span asp-validation-for="FirstName" class="text-danger"></span>
</div>
</div>
Can I display the input box for emeregencyInfo, emailAddress and phone number in above view. I want to show both the input box for emailAddress and PhoneNumber in the same EmployeeInfo view so that users can input their information.
Any help will be highly appreciated.
You can create a compound class and pass it to the view as data model:
public class Info
{
public EmployeeInfo? Employee { get; set; }
public EmergencyInfo? Emergency { get; set; }
}
The view code:
#model Info;
#{
ViewData["Title"] = "Create";
}
<form method="post" asp-action="Create">
<div class="form-group row">
<div class="col-sm-4">
<label asp-for="#Model.Employee.LastName" class="control-label"></label>
<input asp-for="#Model.Employee.LastName" class="form-control input-lg" />
<span asp-validation-for="#Model.Employee.LastName" class="text-danger"></span>
</div>
...
<button type="submit">Save</button>
</form
On the controller side:
[HttpPost]
public IActionResult Create(Info data)
{
// Processing the entered data
....
return View(data);
}

Class object property model is null?

I am building a to-do-list project as a practice. It has one relationship to the Member model and the Member model has many relationships to 'to-do-list'
Member controller create method works without any issue but the to-do-list controller throws model state is invalid on Member object property of to-do-list
ToDoList
using System.ComponentModel.DataAnnotations;
namespace To_Do_List.Models
{
public class ToDoList
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(200, MinimumLength = 1, ErrorMessage = "To Do List Item cannot be longer than 200 characters.")]
public string Title { get; set; }
public string Description { get; set; }
[DataType(DataType.Date)]
public DateTime DueDate { get; set; }
public string Priority { get; set; }
public int AssignToId { get; set; }
public Member AssignTo { get; set; }
[Required]
[StringLength(15, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.")]
[RegularExpression("^(Completed|Not Completed)$", ErrorMessage = "The status must be Completed or Not Completed")]
public string Status { get; set; }
}
}
Member
using Microsoft.Build.Framework;
namespace To_Do_List.Models
{
public class Member
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
[Required]
public ICollection<ToDoList> ToDoLists { get; set; }
}
}
create method of to do list controller
public async Task<IActionResult> Create([Bind("Id,Title,Description,DueDate,Priority,AssignToId,AssignTo, Status")] ToDoList toDoList)
{
if (ModelState.IsValid)
{
_context.Add(toDoList);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["AssignToId"] = new SelectList(_context.Members, "Id", "Id", toDoList.AssignToId);
return View(toDoList);
}
View method of to do list
public IActionResult Create()
{
return View();
}
Create.cshtml
#model To_Do_List.Models.ToDoList
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>ToDoList</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<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="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DueDate" class="control-label"></label>
<input asp-for="DueDate" class="form-control" />
<span asp-validation-for="DueDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Priority" class="control-label"></label>
<input asp-for="Priority" class="form-control" />
<span asp-validation-for="Priority" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Status" class="control-label"></label>
<input asp-for="Status" class="form-control" />
<span asp-validation-for="Status" 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");}
}
Member controller create method works without any issue but the
to-do-list controller throws model state is invalid on Member object
property of to-do-list
Well, I have succssfully reproducced your issue and your ModelState.IsValid false is logical. Because, when you leave public Member AssignTo { get; set; } with default annotation it means required. whilist, you haven't pass any property from your view consequently, your bindings always be false as you have defined it into the [Bind] property that is AssignTo However, value has not been pass to it.
How to resolve:
In this scenario, you either has to pass AssignTo to your create action or make it nullable using ? annotation as following:
public Member? AssignTo { get; set; }
Note: If you don't want to set AssignTo as nullable then you have to pass all property value from your view as following:
<div class="form-group">
<label asp-for="AssignTo.Name" class="control-label"></label>
<input asp-for="AssignTo.Name" class="form-control" />
<span asp-validation-for="AssignTo.Name" class="text-danger"></span>
</div>
Here, I am passing only AssignTo.Name you have to pass rest of the values.
Output:
Note: If you would like to know more details on it you could check our official document here.

Why File upload is stopped working after convert multiple models into a view model entity framework?

Controller
public async Task<IActionResult> Create(IFormFile? StaffPhoto, CollectionViewModel collectionModel)
{
if (StaffPhoto != null){...} // issue is StaffPhoto value is null
}
View Model
namespace Website.Models
{
public class CollectionViewModel
{
public Staff staff { get; set; }
public Contact contact { get; set; }
}
}
Entity Model
public class Staff
{
public int StaffId { get; set; }
[DisplayName("First Name")]
[Required]
public string StaffFirstName { get; set; }
[DisplayName("Last Name")]
[Required]
public string StaffLastName { get; set; }
[DisplayName("Photo")]
public string? StaffPhoto { get; set; }
}
View
#model CollectionViewModel
<form asp-action="Create" enctype="multipart/form-data" method="post" class="row g-3 mt-0">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="col">
<label asp-for="staff.StaffFirstName" class="form-label"></label>
<input asp-for="staff.StaffFirstName" class="form-control" />
<span asp-validation-for="staff.StaffFirstName" class="text-danger"></span>
</div>
<div class="col">
<label asp-for="staff.StaffLastName" class="form-label"></label>
<input asp-for="staff.StaffLastName" class="form-control" />
<span asp-validation-for="staff.StaffLastName" class="text-danger"></span>
</div>
<div class="col-md-3">
<label asp-for="staff.StaffPhoto" class="form-label"></label>
<input asp-for="staff.StaffPhoto" type="file" accept="image/*" class="form-control" />
<span asp-validation-for="staff.StaffPhoto" class="text-danger"></span>
#{if (ViewBag.fileUploadErrorMessage != null)
{
<span class="text-danger">#ViewBag.fileUploadErrorMessage</span>
}
}
</div>
<div class="col">
<input type="submit" value="Create" class="btn btn-primary" />
<a asp-action="Create" class="btn btn-secondary">Reset All</a>
</div>
</form>
You should add IFormFile in model.
public class CollectionViewModel
{
public Staff staff { get; set; }
public IFormFile StaffPhoto { get; set; }
public Contact contact { get; set; }
}
set StaffPhoto to asp-for in view.
<input asp-for="StaffPhoto" type="file" accept="image/*" class="form-control" />

ASP.NET core "create" action

I am creating a web app using asp.net mvc.
I used the default scaffolding to create the controller and the view (I have user table and game table ofc database is created beforehand)
when I try to create new entity in the game table using the default create form it doesn't submit the data.
when I do the same in the user table it works fine.
the problem is that its the default code generated by visual studio and I'm not able to figure out what could be the cause.
this is the controller create action :
// GET: Games/Create
public IActionResult Create()
{
return View();
}
// POST: Games/Create
// To protect from overposting attacks, enable the specific properties you want to bind to, for
// more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ID,Name,ReleaseDate,Genre,ageRestriction,Developer,Description")] Game game)
{
if (ModelState.IsValid)
{
_context.Add(game);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(game);
}
this is the create view :
#model GameSense.Models.Game
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Game</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ReleaseDate" class="control-label"></label>
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Genre" class="control-label"></label>
<input asp-for="Genre" class="form-control" />
<span asp-validation-for="Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ageRestriction" class="control-label"></label>
<input asp-for="ageRestriction" class="form-control" />
<span asp-validation-for="ageRestriction" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Developer" class="control-label"></label>
<input asp-for="Developer" class="form-control" />
<span asp-validation-for="Developer" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" 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>
and this is the model for "game" :
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace GameSense.Models
{
public class Game
{
public int ID { get; set; }
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Required]
public string Genre { get; set; }
[Required]
public int ageRestriction { get; set; }
[Required]
public string Developer { get; set; }
[Required]
public Coordinates DeveloperLocation { get; set; }
[Required]
public string Description { get; set; }
}
}
as I said, its very "defaulty" i just want to see that everything works before i continue..
any ideas on what might be going on and why i cant create new "Game" items in the database using the default form?
You've set the Coordinates property as Required in your model, but you aren't asking the user to enter any value(s) for that in the form. So it will always be null (and thus always fail validation).
You can either remove the [Required] attribute from the property, or remove the property from the model entirely, or build some UI to allow the field to be populated by the user.

View post form return null?

I am having problems to return a model using a form.
The problem is when I submit the form, the values are null even though I've specified that returns a model
This is my controller
And this is my View that returns null.
#model MyEnglishDictionary.Models.Dictionary
#{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<form method="post" asp-action="Create">
<div class="p-4 border rounded">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group row">
<div class="col-2">
<label asp-for="Word"></label>
</div>
<div class="col-5">
<input asp-for="Word" class="form-control" />
</div>
<span asp-validation-for="Word" class="text-danger"></span>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="Meaning"></label>
</div>
<div class="col-5">
<input asp-for="Meaning" class="form-control" />
</div>
<span asp-validation-for="Meaning" class="text-danger"></span>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="Pronunciation"></label>
</div>
<div class="col-5">
<input asp-for="Pronunciation" class="form-control" />
</div>
<span asp-validation-for="Pronunciation" class="text-danger"></span>
</div>
<br />
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Create" />
<a asp-action="Index" class="btn btn-success">Back To List</a>
</div>
</div>
</form>
#section Scripts{
#{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}
EDIT
This is my Dictionary controller.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MyEnglishDictionary.Data;
using MyEnglishDictionary.Models;
namespace MyEnglishDictionary.Controllers
{
public class DictionaryController : Controller
{
private readonly ApplicationDbContext _db;
public DictionaryController(ApplicationDbContext db)
{
_db = db;
}
public IActionResult Index()
{
return View(_db.Dictionaries.ToList());
}
public IActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Models.Dictionary word)
{
if(!ModelState.IsValid)
{
return View(word);
}
_db.Add(word);
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
}
}
And this is my Dictionary model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyEnglishDictionary.Models
{
public class Dictionary
{
[Key]
public int Id { get; set; }
[Required]
public string Word { get; set; }
[Required]
public string Meaning { get; set; }
[Required]
public string Pronunciation { get; set; }
public string Link { get; set; }
public DateTime Date { get; set; }
}
}
I am using Net Core 2.1, but I have some few projects that I use the same way to pass the form model from View to controller and they work.
You need to pay attention to the name of the parameter and fields.
For your issue, it is caused by that you defined a field which is Word and the parameter is word which caused the binding failed.
Try to change the public async Task<IActionResult> Create(Models.Dictionary word) to public async Task<IActionResult> Create(Models.Dictionary dictionary).
change the word parameter name to something else like _word, it seems like the compiler doesn't accept it as a parameter name in c#.
public async Task<IActionResult> Create(Models.Dictionary _word)
{
if(!ModelState.IsValid)
{
return View(_word);
}
_db.Add(_word);
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
btw, I didn't see it in the reserved keywords list
You have to bind the properties to your model. In your case:
public async Task<IActionResult> Create([Bind("Word, Meaning, Pronounciation")] Dictionary word)
Further reading:
Model Binding

Categories