Custom validation message not shown using IValidatableObject - validation

I have a simple form with 2 buttons (Cancel and Submit) and a TextArea. The user types a list of email addresses and presses submit. I am trying to figure out why my custom message is not being shown when I submit my form. I know the validation logic works as it triggers my [Required] rule and I can see the error message for that:
However, when I type in data such as "test#" and then submit, the logic in the Validate gets triggered but I can't see my error message "Please make sure that all of the emails are valid". What am I doing wrong?
That is my Model:
public class ShareModel : IValidatableObject
{
[HiddenInput] public string Title { get; set; }
[Required]
public string Emails { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// custom validation logic here
yield return new ValidationResult($"Please make sure that all of the emails are valid", new[] { "Emails" });
}
}
That is my view:
<div class="modal fade" id="shareFormModal" role="dialog">
<div class="modal-dialog modal-md">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Share Workbook - #Model.Title</h4>
</div>
#using (Html.BeginForm("ShareWorkbook", "Home", FormMethod.Post, new {#id = "partialform"}))
{
<div class="modal-body">
<label>#BaseLanguage.Share_workbook_Instruction_text</label>
<div class="form-group">
<textarea class="form-control" asp-for="Emails" rows="4" cols="50" placeholder="#BaseLanguage.ShareDialogPlaceholder"></textarea>
<span asp-validation-for="Emails" class="text-danger"></span>
</div>
<input asp-for="Title"/>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Share</button>
<button id="btnCancelDialog" type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>

According to this post IValidatableObject is not considered on client-side. In order to display custom error message from custom validation you need to implement custom ValidationAttribute that also implements IClientModelValidator interface as described here.

For future reference, as Alexander explained above I had to use both ValidationAttribute, IClientModelValidator like that:
ShareModel:
public class ShareModel
{
[HiddenInput] public string Title { get; set; }
[Required]
[IsEmailAttribute(ErrorMessage = "Check all of the emails you have typed")]
public string Emails { get; set; }
}
public class IsEmailAttribute : ValidationAttribute, IClientModelValidator
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
return new ValidationResult("Check emails!");
}
public void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
MergeAttribute(context.Attributes, "data-val-isEmail", errorMessage);
}
private bool MergeAttribute(
IDictionary<string, string> attributes,
string key,
string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
_ShareView.cshtml:
#using DNAAnalysisCore.Resources
#model DNAAnalysisCore.Models.ShareModel
<!-- Modal -->
<div class="modal fade" id="shareFormModal" role="dialog">
<div class="modal-dialog modal-md">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Share Workbook - #Model.Title</h4>
</div>
#using (Html.BeginForm("ShareWorkbook", "Home", FormMethod.Post, new {#id = "partialform"}))
{
<div class="modal-body">
<label>#BaseLanguage.Share_workbook_Instruction_text</label>
<div class="form-group">
<textarea class="form-control" asp-for="Emails" rows="4" cols="50" placeholder="#BaseLanguage.ShareDialogPlaceholder"></textarea>
<span asp-validation-for="Emails" class="text-danger"></span>
</div>
<input asp-for="Title"/>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Share</button>
<button id="btnCancelDialog" type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
</div>
index.cshtnl:
#using DNAAnalysisCore.Resources
#model DNAAnalysisCore.Models.WorkBookModel
#{
}
#section BodyFill
{
<div id="shareFormContainer">
<!--PLACEHOLDER FOR SHARE DIALOG -->
#{
#Html.Partial("_ShareView", new ShareModel())
}
</div>
<div class="workbook-container">
<table class="table">
<tbody>
#foreach (var workbook in Model.Workbooks)
{
<tr>
<td>#Html.ActionLink(workbook.Name, "Open", "OpenAnalytics", new { id = Model.Id, workbook = workbook.Name })</td>
<td>
<button title="Share" class="share-button" onclick='showSharingView("#workbook.Name")'> </button>
</td>
</tr>
}
</tbody>
</table>
</div>
}
#section Scripts
{
<!--Load JQuery 'unobtrusive' validation -->
#await Html.PartialAsync("_ValidationScriptsPartial")
<script type="text/javascript">
function showSharingView(title) {
var url = "#Url.Action("ShowShareDialog", "Home")" + "?workbookTitle=" + encodeURI(title);
$('#shareFormContainer').load(url,
function() {
$('#shareFormModal').modal("show");
// // We need to manually register the form for validation as the dialog is
// // not included in the page when it initially loads
$.validator.unobtrusive.parse("#partialform");
// email validation
$.validator.addMethod("isEmail",
function (value, element, parameters) {
// TODO CLIENT SIDE VALIDATETION LOGIC HERE
return false;
});
$.validator.unobtrusive.adapters.add("isEmail",
[],
function(options) {
options.rules.isEmail = {};
options.messages["isEmail"] = options.message;
});
});
}
</script>
}

Related

Why does ASP.NET Core 7 MVC ModelValidator always set ModelState.IsValid to true when the Model is invalid

I'm converting an ASP.NET 4.8 MVC web site to ASP.NET Core 7 MVC. I'm running into a problem with ModelValidation where my ModelState is always valid, even when it is not. I'm using the pattern that was used in .Net 4x and worked fine. Microsoft Learn link
Controller
[HttpPost]
public async Task<IActionResult> Create(TcPhoneType tcPhoneType)
{
if (ModelState.IsValid)
{
await _phoneTypeService.CreatePhoneType(tcPhoneType);
return RedirectToAction("Index");
}
return View(tcPhoneType);
}
Model.cs
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
#nullable disable
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace App.Models;
public partial class TcPhoneType
{
public string PhoneTypeId { get; set; }
public string PhoneTypeDescription { get; set; }
public virtual ICollection<MemberPhone> MemberPhones { get; } = new List<MemberPhone>();
}
ModelMetadata.cs
using System.ComponentModel.DataAnnotations;
namespace App.Models;
[MetadataType(typeof(TcPhoneTypeMetadata))]
public partial class TcPhoneType
{
}
public class TcPhoneTypeMetadata
{
[Required(AllowEmptyStrings = false, ErrorMessage = "Item is required")]
[StringLength(1)]
public string? PhoneTypeId { get; set; }
[Required(AllowEmptyStrings = false)]
[StringLength(5)]
public string? PhoneTypeDescription { get; set; }
}
Create.cshtml
#model SeasonCourt7.Models.TcPhoneType
#{
ViewBag.Title = "Add Phone Type Code";
}
<h2>#(ViewBag.Title)</h2>
<br>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="row mb-3">
<div class="col-sm-2">
<label asp-for="PhoneTypeId" class="col-form-label">Code</label>
</div>
<div class="col-sm-10">
<input asp-for="PhoneTypeId" class="form-control" autofocus />
</div>
<span asp-validation-for="PhoneTypeId" class="text-danger"></span>
</div>
<div class="row mb-3">
<div class="col-sm-2">
<label asp-for="PhoneTypeDescription" class="col-form-label">Description</label>
</div>
<div class="col-sm-10">
<input asp-for="PhoneTypeDescription" class="form-control" />
</div>
<span asp-validation-for="PhoneTypeDescription" class="text-danger"></span>
</div>
<div class="row mb-3">
<div class="offset-sm-2 col-sm-10">
<input type="submit" value="Create" class="btn btn-primary" />
Back
</div>
</div>
</div>
}
#section Scripts {
<partial name="_ValidationScriptsPartial" />
}
I set a breakpoint on the if (ModelState.IsValid) line so I could inspect the model and ModelState. I can see the data that is bound to the model is as expected and the ModelState is always valid.
If I pass null data in the model, I should see the ModelState.IsValid = false, but it's always true.
The model.cs is generated by EF and could be regenerated in the future which makes it impractical to add the metadata attributes for validation, as the file could be regenerated in the future.
The only way I've been able to get the ModelValidation to work as expected, is to decorate the model directly with the validation attributes.

ASP.NET MVC - data validation for modal window doesn't work

In my MVC application I have simple model for project:
public class Project
{
public int Id { get; set; }
[Required]
[MinLength(5, ErrorMessage = "Project name must have at leat 5 characters")]
public string ProjectName { get; set; }
[Required]
[MinLength(10, ErrorMessage = "Project description name must have at leat 10 characters")]
public string ProjectDescription { get; set; }
public ICollection<BugTrackerUser> Users { get; set; }
public Project()
{
Users = new List<BugTrackerUser>();
}
}
On the index page I have button that triggers bootstrap modal window. As a button, modal itself located in Index.chtml, but form that belongs to this model located in partial view _addProjectPartialView:
<form method="post" id="projectForm">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="ProjectName">Project Name</label>
<input asp-for="ProjectName" class="form-control" />
<span asp-validation-for="ProjectName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProjectDescription">Project Description</label>
<input asp-for="ProjectDescription" class="form-control" />
<span asp-validation-for="ProjectDescription" class="text-danger"></span>
</div>
In HomeController I have this post method for adding new project:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult AddProject(Project project)
{
if (ModelState.IsValid)
{
_repository.AddProject(project);
return RedirectToAction("Index", "Home");
}
return PartialView("_addProjectPartialView", project);
}
Logic of adding new project works fine, and I have all references that i need, jquery, jquery-validation and jquery-validation-unobtrusive, but I have problems with validation. Validation errors doesn't displays in my modal window. What can I try to solve this problem? I already read a bunch of tutorials, related questions etc, but it seems to me that there is no problems with code. I only have doubts with my return statement in AddProject method, should I return something else?
I solved this problem by removing partial view. Form that I Have in partial view and modal window I placed in Index.cshtml, also instead of asp.net tag helpers I use Html.BeginForm, and it works fine. Here is my modal:
<div class="modal fade" id="addProjectModal" tabindex="-1" aria-labelledby="addProjectModallLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
#using (Html.BeginForm("AddProject", "Home", FormMethod.Post))
{
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Project Information</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(p => p.Id)
<div class="form-group">
#Html.LabelFor(p => p.ProjectName, htmlAttributes: new { #class = "control-label col-3" })
<div class="col-9">
#Html.TextBoxFor(p => p.ProjectName, new { #class = "form-control" })
#Html.ValidationMessageFor(p => p.ProjectName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(p => p.ProjectDescription, htmlAttributes: new { #class = "control-label col-3" })
<div class="col-9">
#Html.TextBoxFor(p => p.ProjectDescription, new { #class = "form-control" })
#Html.ValidationMessageFor(p => p.ProjectDescription, "", new { #class = "text-danger" })
</div>
</div>
#*<partial name="_addProjectPartialView" />*#
</div>
<div class="modal-footer">
<div>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary" id="btnSave">Save</button>
</div>
</div>
}
</div>
</div>
I also using ajax post method to add projects and render list of projects async on the client side:
<script type="text/javascript">$(document).ready(function () {
$('#btnSave').click(function () {
var projectData = $('#projectForm').serialize();
$.ajax({
type: "POST",
url: "/Home/AddProject",
data: projectData,
success: function () {
window.location.href = "/Home/Index";
}
})
})
})
$('#closeButton').click(function () {
$('#addProjectModal').modal('hide');
})</script>
But I still want to understand how to solve this problem, because using partial views have some benefits
I think the problem here is , when you triggers the modal that modal doesn't have the references to (validator unobtrusive).
So try manually register this where you triggers the modal like this
$.validator.unobtrusive.parse("#idForModal");

How to show Validation Summary in a modal form with ajax post method?

I'm beginner in ASP.NET Core MVC. I have a problem in my website code.
My model is User that has some fields, and I wrote a view model based on these fields.
I've written the following code:
My view model: RegisterViewModel:
public class RegisterViewModel
{
[Display(Name = "Username")]
[Required(ErrorMessage = "Enter {0} value please.")]
[MaxLength(20, ErrorMessage = "{0} Shouldn't have more than {1} Characters")]
public string Username { get; set; }
[Display(Name = "Password")]
[Required(ErrorMessage = "Enter {0} value please.")]
[MaxLength(50, ErrorMessage = "{0} Shouldn't have more than {1} Characters")]
public string Password { get; set; }
}
My controller: AccountController:
public class AccountController : Controller
{
private IUser _iuser;
public AccountController(IUser iuser)
{
_iuser = iuser;
}
public IActionResult Register()
{
return View();
}
[HttpPost]
public IActionResult Register(RegisterViewModel register)
{
if (ModelState.IsValid)
{
if (_iuser.isUsernameExist(register.Username))
{
ModelState.AddModelError("UserName", "This User Exists!");
return PartialView(register);
}
else
{
User user = new User()
{
Username = register.Username,
Password = HashGenerators.EncodingPassWithMD5(register.Password),
RoleId = 2,
};
_iuser.AddUser(user);
string TabName = "UserTab";
return Json(new { redirectToUrl = Url.Action("Index", "Profile", new {TabName}) });
}
}
else
{
return PartialView(register);
}
}
}
Register action has a view that has displayed as a modal.
View Register.cshtml:
<div class="row">
<div class="col-md-8 col-md-offset-2">
<hr />
<form asp-action="Register">
<div asp-validation-summary="ModelOnly" class="text-danger text-right"></div>
<div class="form-group">
<input asp-for="Username" class="form-control" , placeholder="username" id="txtUsername"/>
<span asp-validation-for="Username" class="text-danger" id="ValidationSummery"></span>
</div>
<div class="form-group">
<input asp-for="Password" class="form-control" , placeholder="password" id="txtPassword"/>
<span asp-validation-for="Password" class="text-danger text-right" id="ValidationSummery"></span>
</div>
<div class="form-group">
<input type="button" value="Create" onclick='AddUser()' class="btn-red pull-left" />
<button href="#" type="button" onclick='ClearForm()' class="btn-red pull-right"> Clear Form </button>
</div>
</form>
</div>
</div>
Modal code (at the end of above code):
<div id="myModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div id="bodyModal" class="modal-body">
</div>
</div>
</div>
</div>
and at the end of view, I have these Ajax scripts:
<script>
function ClearForm() {
$.ajax({
url: "/Account/Register/",
type: "Get",
data: {}
}).done(function (result) {
$('#myModal').modal('show');
$('#bodyModal').html(result);
});
$('#myModal').modal('dispose'); }
</script>
<script>
function AddUser() {
$.ajax({
url: "/Account/Register/",
type: "Post",
data: {
Username: $("#txtUsername").val(),
Password: $("#txtPassword").val(),
},
success: function (response) {
window.location.href = response.redirectToUrl;
}
}).done(function (result) {
$('#myModal').modal('show');
$('#bodyModal').html(result);
});}
</script>
When program is running, the ClearForm button works well, the Create button works well when modelstate is valid. But when modelstate is not valid, it goes to an error page (Hello world!), I see validation errors for 1 second but browser opens an wrong page and doesn't stay on modal for showing validation error.
NOTE: When I delete: ( window.location.href = response.redirectToUrl;) from ajax AddUser function at success part, modal stays open and validation errors displays. But if modelstate is valid, modal stays open but it was empty and destination page(Index/Profile#UserTab) doesn't display.
Please help me, how can I change above code to solve this problem?
I think you can have a new partial view to show the validation message.
Add a hidden input field to the partial view:
<input name="IsValid" type="hidden" value="#ViewData.ModelState.IsValid.ToString()" />
Then in ajax success function determine whether to show the modal or redirect by judging the value of IsValid
A simple demo based on your codes.
Register.cshtml:
#model RegisterViewModel
#{
ViewData["Title"] = "Register";
}
<h1>Register</h1>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<hr />
<form asp-action="Register">
<div asp-validation-summary="ModelOnly" class="text-danger text-right"></div>
<div class="form-group">
<input asp-for="Username" class="form-control" , placeholder="username" id="txtUsername" />
<span asp-validation-for="Username" class="text-danger" id="ValidationSummery"></span>
</div>
<div class="form-group">
<input asp-for="Password" class="form-control" , placeholder="password" id="txtPassword" />
<span asp-validation-for="Password" class="text-danger text-right" id="ValidationSummery"></span>
</div>
<div class="form-group">
<input type="button" value="Create" onclick='AddUser()' class="btn-red pull-left" />
<button href="#" type="button" onclick='ClearForm()' class="btn-red pull-right"> Clear Form </button>
</div>
</form>
</div>
</div>
<div id="myModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div id="bodyModal" class="modal-body">
</div>
</div>
</div>
</div>
#section scripts{
<script>
function AddUser() {
$.ajax({
url: "/Account/Register/",
type: "Post",
data: {
Username: $("#txtUsername").val(),
Password: $("#txtPassword").val(),
},
success: function (response) {
$('#bodyModal').html(response);
var isValid = $('body').find('[name="IsValid"]').val() == 'True';
if (!isValid) {
$('#myModal').modal('show');
} else {
window.location.href = "#Url.Action("Index", "Profile")";
}
}
});
}
</script>
}
_Register.cshtml(Partial View):
#model RegisterViewModel
<form asp-action="Register">
<input name="IsValid" type="hidden" value="#ViewData.ModelState.IsValid.ToString()" />
<div asp-validation-summary="ModelOnly" class="text-danger text-right"></div>
<div class="form-group">
<input asp-for="Username" class="form-control" , placeholder="username" id="txtUsername" readonly />
<span asp-validation-for="Username" class="text-danger" id="ValidationSummery"></span>
</div>
<div class="form-group">
<input asp-for="Password" class="form-control" , placeholder="password" id="txtPassword" readonly />
<span asp-validation-for="Password" class="text-danger text-right" id="ValidationSummery"></span>
</div>
</form>
Controller:
public class AccountController : Controller
{
public IActionResult Register()
{
return View();
}
[HttpPost]
public IActionResult Register(RegisterViewModel register)
{
if (ModelState.IsValid)
{
// do some stuff
}
return PartialView("_Register",register);
}
}
Result:
With inspiring help of https://stackoverflow.com/users/11965297/mj1313, I Changed my code like following and my problem is solved entirely...
In controller, I send a parameter(Valid) by ViewBag to my view, to specify ModelState validity status.
[HttpPost]
public IActionResult Register(RegisterViewModel register)
{
if (ModelState.IsValid)
{
if (_iuser.isUsernameExist(register.Username))
{
ViewBag.Valid= "1"; // It's part of my change
ModelState.AddModelError("UserName", "This User Exists!");
return PartialView(register);
}
else
{
User user = new User()
{
Username = register.Username,
Password = HashGenerators.EncodingPassWithMD5(register.Password),
RoleId = 2,
};
_iuser.AddUser(user);
ViewBag.Valid= "0"; // It's part of my change
string TabName = "UserTab";
return Json(new { redirectToUrl = Url.Action("Index", "Profile", new {TabName}) });
}
}
else
{
ViewBag.Valid= "1"; // It's part of my change
return PartialView(register);
}
}
And in My view, I use a hidden input for save ViewBag.
<form asp-action="Register">
<input name="IsValid" type="hidden" value="#ViewBag.Valid" /> #*It's part of my change*#
<div asp-validation-summary="ModelOnly" class="text-danger text-right"></div>
<div class="form-group">
<input asp-for="Username" class="form-control" , placeholder="username"
</div>
.....
</form>
At the end, I change Ajax function, success part:
function AddUser() {
$.ajax({
url: "/Account/Register/",
type: "Post",
data: {
Username: $("#txtUsername").val(),
Password: $("#txtPassword").val(),
},
success: function (response) {
$('#bodyModal').html(response);
var isValid = $('body').find('[name="IsValid"]').val();
if (isValid) {
$('#myModal').modal('show');
} else {
window.location.href = response.redirectToUrl;
}
}
}).done(function (result) {
$('#myModal').modal('show');
$('#bodyModal').html(result);
});}

Remote validation fails with IFormFile attribute

I notice that remote validation in ASP.NET core always fails because the server always receives a null IFormFile in the controller method. Is there a way to make it work?. Some code to reproduce the problem:
The model class ("not mapped" was included so Entity Framework doesn't interfere, but it also didn't work in another project without Entity Framework.
public class Movie
{
public int ID { get; set; }
[Sacred(sacredWord: "sonda")]
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
[Remote(action: "VerifyRating", controller: "Movies")]
public string Rating { get; set; }
[NotMapped]
[Remote(action: "VerifyFile", controller: "Movies"),Required]
public IFormFile File { get; set; }
}
The controller
public class MoviesController : Controller
{
private readonly WebAppMVCContext _context;
public MoviesController(WebAppMVCContext context)
{
_context = context;
}
// GET: Movies/Create
public IActionResult Create()
{
return View();
}
[AcceptVerbs("Get", "Post")]
public IActionResult VerifyFile(IFormFile File)
{
if(File == null)
{
return Json("The file is null");
}
else
{
return Json("The file is not null");
}
}
// POST: Movies/Create
// To protect from overposting attacks, please 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,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (ModelState.IsValid)
{
_context.Add(movie);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(movie);
}
[AcceptVerbs("Get", "Post")]
public IActionResult VerifyRating( int rating)
{
if(rating>0 && rating < 10)
{
return Json(true);
}
else
{
return Json($"The rating is invalid");
}
}
and the View
#model WebAppMVC.Models.Movie
#{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<h4>Movie</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="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="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Rating" class="control-label"></label>
<input asp-for="Rating" class="form-control" />
<span asp-validation-for="Rating" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="File" class="control-label"></label>
<input asp-for="File" />
<span asp-validation-for="File" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts{
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
$.validator.addMethod('sacred',
function (value, element, params) {
var title = $(params[0]).val(),
sacredword = params[1];
if (title!=null && title == sacredword) {
return true;
}
else {
return false;
}
}
);
$.validator.unobtrusive.adapters.add('sacred',
['sacredword'],
function (options) {
var element = $(options.form).find('input#Title')[0];
options.rules['sacred'] = [element, options.params['sacredword']];
options.messages['sacred'] = options.message;
}
);
</script>
}
Notice that all the other validations work (including the remote validation "VerifyRating").

ApplicationDbContext.Update() doesn't update but saves it as a new record

I am playing in the new APS.NET 5 RC1 environment but the default edit action creates a new entity of the object. I am searched the whole day finding where it goes wrong but I can't find out why it goes wrong.
Controller:
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.Data.Entity;
using SSTv5.Models;
using SSTv5.Models.Organisations;
using SSTv5.Models.Views;
using System.Collections.Generic;
using SSTv5.Models.Global;
namespace SSTv5.Controllers
{
public class OrganisationTypesController : Controller
{
private ApplicationDbContext _context;
private List<Breadcrumb> _bcList = new List<Breadcrumb>();
public OrganisationTypesController(ApplicationDbContext context)
{
_context = context;
}
#region Edit
// GET: OrganisationTypes/Edit/5
public async Task<IActionResult> Edit(int? id)
{
_bcList.Add(new Breadcrumb("OrganisationTypes", true, "Index", "Index"));
_bcList.Add(new Breadcrumb("Edit", false));
ViewBag.BcList = _bcList;
if (id == null)
{
return HttpNotFound();
}
OrganisationType organisationType = await _context.OrganisationType.SingleAsync(m => m.OrganisationTypeId == id);
if (organisationType == null)
{
return HttpNotFound();
}
return View(organisationType);
}
// POST: OrganisationTypes/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(OrganisationType organisationType)
{
if (ModelState.IsValid)
{
_context.Update(organisationType);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(organisationType);
}
#endregion
}
}
Form:
#model SSTv5.Models.Organisations.OrganisationType
#{
ViewData["Title"] = "Edit";
}
<h2>Edit</h2>
<form asp-action="Edit">
<div class="form-horizontal">
<h4>OrganisationType</h4>
<hr />
<div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="OrganisationTypeName" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="OrganisationTypeName" class="form-control" />
<span asp-validation-for="OrganisationTypeName" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="ClassIcon" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="ClassIcon" id="classIcon" />
<span asp-validation-for="ClassIcon" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Edit" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
}
For anyone else who has this problem the answer is quite simple.
The form doesn't send the ID of the object with it. The only thing you need to do is put the id in a hidden field in the form. Then it will work fine.

Resources