IValidatableObject.Validate does not fire if DataAnnoations add ValidationResult - asp.net-mvc

With a standard ASP.NET MVC controller and view and a model that both implements IValidatableObject and has DataAnnotations, the Validate method never fires if the DataAnnotations generate an exception.
Here's the model...
public class ModelStaticDA : IValidatableObject {
public long Id { get; set; }
[EmailAddress]
public string EmailAddress { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
yield return new ValidationResult("MODEL NOT VALID!")
}
}
Here's the view (client validation is disabled for this demo)...
#model BindingAndValidation.Models.ModelStaticDA
#{
ViewBag.Title = "Create";
HtmlHelper.ClientValidationEnabled = false;
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ModelStaticDA</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.EmailAddress, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.EmailAddress)
#Html.ValidationMessageFor(model => model.EmailAddress)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
If you post something like "invalid" to EmailAddress, only the DataAnnotation message displays. If you post a valid e-mail address, the message from Validate displays.
Is this the correct behavior? If so, why? If not, what am I doing wrong?

You are doing everything right, that's the behavior. My guess it was designed this way to avoid having to validate again while working with the properties inside the Validate method, you know that when it's called you are working with valid data, and you can do things that require valid data.

Related

How to make your button on a form using asp.net-mvc work?

I have a button, it does not seem to create new users to my database. What it does it only inherits user validaton to my Login method and need some guidance to this please and thanks. Below is the logic what i am trying to do. What i want to do my create button must be able to create new users if not exist to the database.
Controller:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateModel objSubmit)
{
ViewBag.Msg = "Details submitted successfully";
return View(objSubmit);
}
// This is for login, and its hits this method each time.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(Login login)
{
if (ModelState.IsValid)
{
bool success = WebSecurity.Login(login.username, login.password, false);
var UserID = GetUserID_By_UserName(login.username);
var LoginType = GetRoleBy_UserID(Convert.ToString(UserID));
if (success == true)
{
if (string.IsNullOrEmpty(Convert.ToString(LoginType)))
{
ModelState.AddModelError("Error", "Rights to User are not Provide Contact to Admin");
return View(login);
}
else
{
Session["Name"] = login.username;
Session["UserID"] = UserID;
Session["LoginType"] = LoginType;
if (Roles.IsUserInRole(login.username, "Admin"))
{
return RedirectToAction("AdminDashboard", "Dashboard");
}
else
{
return RedirectToAction("UserDashboard", "Dashboard");
}
}
}
else
{
ModelState.AddModelError("Error", "Please enter valid Username and Password");
return View(login);
}
}
else
{
ModelState.AddModelError("Error", "Please enter Username and Password");
return View(login);
}
}
Model:
namespace eNtsaPortalWebsiteProject.Models
{
public class CreateModel
{
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string password { get; set; }
[Required]
public string username { get; set; }
}
}
// View for login
<div data-="mainContent">
<section class="container">
<div class="logo col-sm-12 text-center col-md-12"> <img alt="" src="~/Images/eNtsa.png" /></div>
<div class="clearfix"></div>
<div class="container">
<div class="row">
<div id="MyWizard" class="formArea LRmargin">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div id="divMessage" class="text-center col-md-12 col-md-offset-12 alert-success">
#Html.ValidationSummary()
</div>
<div class="col-md-12 col-md-offset-10 col-xs-12">
<div class="loginPage panel-info">
<div class="form-group"><span class=""><i class="glyphicon glyphicon-user">Username:</i></span>
#Html.TextBoxFor(model => model.username, new { #class = "form-control text-center", autocomplete = "off" })
#Html.ValidationMessageFor(model => model.username)
</div>
<div class="form-group">
<span class=""><i class="glyphicon glyphicon-lock">Password:</i></span>
#Html.PasswordFor(model => model.password, new { #class = "form-control text-center", autocomplete = "off" })
#Html.ValidationMessageFor(model => model.password)
</div>
</div>
<div class="form-group">
<input id="BtnLogin" type="submit" class="btn btn-success btn-pressure" name="BtnLogin" value="Login" />
<input type ="Submit" class="btn btn-info btn-pressure" name="BtnCreate" value="Create" />
</div>
</div>
}
<div class="clear"></div>
</div>
</div>
</div>
</section>
</div>
View for creating user:
<div class="mainContent">
<section class="container">
<div class="logo col-sm-12 text-center col-md-10">
<img alt="" src="~/Images/eNtsa.png"/>
</div>
<div class="container">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div id="divMessage" class="text-center col-md-12 col-md-offset-12 alert-success">
#Html.ValidationSummary()
</div>
<div class="col-md-12 col-md-offset-10 col-xs-12">
<div class="glyphicon-registration-mark">
<div class="form-group"><span class=""><i class="glyphicon glyphicon-user">Username:</i></span>
#Html.TextBoxFor(model=>model.username, new {#class ="form-control text-center", automplete="off" })
#Html.ValidationMessageFor(model=>model.username)
</div>
<div class="form-group">
<span class=""><i class="glyphicon glyphicon-lock">Password:</i></span>
#Html.PasswordFor(model=>model.password, new {#class = "form-control text-center", autocomplete="off" })
#Html.ValidationMessageFor(model=>model.password)
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-success btn-pressure" name="BtnSubmit" value="Submit"/>
</div>
</div>
}
</div>
</section>
</div>
The button is working - that isn't the problem that you're having.
You can have multiple buttons to submit the form but they will return to the same place, either:
a) the controller/action specified in the "action" property of the form
b) if no action is specified then the default location - in your case there isn't one directly specified so it is posting back to the default location.
(see: How to link HTML5 form action to Controller ActionResult method in ASP.NET MVC 4)
The easiest way to accomplish what you're trying to do would be refactor your controller and branch the logic depending on what the value is of the submit button.
(see: MVC razor form with multiple different submit buttons?
and How to handle two submit buttons on MVC view)
This will require some refactoring of the code that you have written, but it is the most straightforward way of achieving what you're trying to do.
In very basic terms it would look something like this:
Model:
namespace eNtsaPortalWebsiteProject.Models
{
public class LoginCreateModel
{
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string password { get; set; }
[Required]
public string username { get; set; }
public string btnSubmit { get; set; } // both buttons will have the same name on your form, with different values ("Create" or "Login")
}
}
Controller:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginCreateModel objSubmit)
{
if (objSubmit.btnSubmit == "Create")
{
// Handle creation logic here
}
if (objSubmit.btnSubmit == "Login")
{
// Handle login logic here
}
return View(objSubmit);
}

How to validate the HTML controls with data annotations in MVC?

In .Net MVC. I have a html control. Inorder to bind it with the model property I am using name attribute. How do we get the validations(using data annotation) provided in the model class property into the html control?
In Cshtml
#using (Html.BeginForm("ClaimWarranty", "WarrentyClaim", FormMethod.Post, new{ enctype = "multipart/form-data" }))
{
<div class="form-group row">
<label for="" class="col-md-2 col-form-label input-label">Email Address:</label>
<div class="col-md-8">
<input type="text" name="Emailaddress" class="form-control input-style" placeholder="example#company.com">
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" onclick="ValidateFileSize()" class="btn btn-default" />
</div>
</div>
}
//The model class is below;
public class ClaimWarranty
{
[Required(ErrorMessage = "Email ID is Required")]
[DataType(DataType.EmailAddress)]
[MaxLength(50)]
[RegularExpression(#"[a-z0-9._%+-]+#[a-z0-9.-]+\.[a-z]{2,4}", ErrorMessage = "Incorrect Email Format")]
public string Emailaddress { get; set; }
}
I am using the name property to bind the text box to the model property .
<input type="text" name="Emailaddress" class="form-control input-style" placeholder="example#company.com">
How do I get the validations in the html control ,provided in the model class (using the data annotations) as shown above without using jquery validations or razor code?
In View
#model Demo.Models.Student
#using (Html.BeginForm("SaveStudent", "Student", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-group">
#Html.LabelFor(model =>model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model =>model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model =>model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btnbtn-primary" />
</div>
</div>
}
In Model
public class Student
{
[Required(ErrorMessage = "Please enter name"), MaxLength(30)]
public string Name { get; set; }
}
By default, ASP.Net MVC framework executes validation logic during model binding. In Controller side, we need to check
if (ModelState.IsValid)
{
}
OR We can also check Individual validation, as shown below:
if (ModelState.IsValidField("LastName") == false)
if(!ModelState.IsValid)
{
// you can get the error information from model state, convert it into list
var validationErrors = ModelState.Values.Where(E => E.Errors.Count > 0)
.SelectMany(E => E.Errors)
.Select(E => E.ErrorMessage)
.ToList();
// now you have got the list of errors, you will need to pass it to view
// you can use view model, viewbag etc
ViewBag.ErrorList = validationErrors;
return View();
}
else
{
// perform your business operation, save the data to database
return View();
}
On View Page -
you have to add check for validation error list
if(ViewBag.ErrorList != null)
{
foreach(var errorMessage in ViewBag.ErrorList)
{
// here you can display the error message and format in html
}
}
Way you can display error on view page
1. #Html.ValidationSummary() - It will display summary of the validation errors
2. #Html.ValidationMessageFor(x => x.Emailaddress) - It will display error message
for specific property
3. you have to manually retrieve the error information from model state and then store it in list and pass to the view page.

Client validation did not work

In my project used ASP.NET MVC 5. I've used System.ComponentModel.DataAnnotations for validation.
I expect when I don't enter value for a mandatory field,be prevent to continue my action.
But this expectation does't satisfy.
How can I solve this problem?
#model Jahan.Blog.ViewModel.ArticleViewModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Article</h4>
<hr />
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.UserId)
#*There are some codes*#
<div class="form-group">
#Html.LabelFor(model => model.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
</div>
#*There are some codes*#
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
ViewModel:
[ModelBinder(typeof(ArticleViewModelBinder))]
public class ArticleViewModel : IArticleViewModel
{
public ArticleViewModel()
{
}
public virtual int Id { get; set; }
[Required]
[StringLength(256)]
public virtual string Title { get; set; }
}
Controller:
[AcceptVerbs(HttpVerbs.Post)]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Prefix = "")]ArticleViewModel articleViewModel, List<int> availableTags)
{
if (ModelState.IsValid)
{
// There are some codes.
}
return View(articleViewModel);
}
open your web.config and find ClientValidationEnabled in appSettings and check if it's set as true :
<add key="ClientValidationEnabled" value="true" />
According to Fiskeboss's comment, jquery.validate.unobtrusive.js library added.Then it works correctly.

Store View form in existing database

Forgive the newbie ASP.NET MVC question. I am used to tutorials where Code First is used with the Entity Framework. Here, this is not the case. I have a form that I want the user to fill out. When it has been filled out, I want to use EF to write the values to an existing database. I can't figure out how to "trap" the values in the view so I can write my EF code. I used a model and I redirected the BeginForm to an "Edit" action method but I don't know how to get my filled in class. Here is the HomeController methods:
[HttpGet]
public ActionResult Trial()
{
UserAccount account = new UserAccount();
return View(account);
}
public ActionResult Edit()
{
}
Here is the model class:
public class UserAccount
{
public int AccountID { get; set; }
public string AccountName { get; set; }
public string RegistrationCode { get; set; }
public DateTime Created { get; set; }
}
}
Here is the View the wizard generated. When I hit the "Create" button, I want to go to the "Edit" action menu or someplace I can use EF to write to the existing database table. How do I do this?
#model AlphaFrontEndService.Models.UserAccount
#{
ViewBag.Title = "Trial";
}
<h2>Trial</h2>
#using (Html.BeginForm("Edit", "Home"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>UserAccount</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.AccountID, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.AccountID)
#Html.ValidationMessageFor(model => model.AccountID)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.AccountName, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.AccountName)
#Html.ValidationMessageFor(model => model.AccountName)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.RegistrationCode, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.RegistrationCode)
#Html.ValidationMessageFor(model => model.RegistrationCode)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Created, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Created)
#Html.ValidationMessageFor(model => model.Created)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
You need a POST action for Trial method like below:
[HttpPost]
public ActionResult Trial(UserAccount model)
{
if (ModelState.IsValid)
{
//Store the form data into your database
}
return View(model);
}
Then in your view, add a submit button element inside the form, also instead of Edit, you just need to use Trial for the postback.
#using (Html.BeginForm("Trial", "Home")) {
//
<input type="submit" value="Submit"/>
}
Note: You don't need to create other Edit action method if you don't have some other reasons.
If you don't know how to save the data to your database, below is an example:
Create your DbContext class
public class MyDbContext : DbContext
{
public MyDbContext()
: base("name=YourDbConnection")
{
}
public DbSet<UserAccount> UserAccounts { get; set; }
}
Then the action method will looks like:
public class HomeController : Controller
{
//
[HttpPost]
public ActionResult Trial(UserAccount model)
{
if (ModelState.IsValid)
{
using (var db = new MyDbContext())
{
db.UserAccounts.Add(model);
db.SaveChanges();
return RedirectToAction("Index");
}
}
return View(model);
}
}

ASP.NET MVC Model Binder Fails with Strings that look like version numbers?

This should be straight-forward, but here goes - we're using MVC4 to handle a multi-part Form request to upload a binary along with some meta-data via a strongly-typed view in MVC4.
One of the fields is a version number for the file (i.e. something like 0.0.6, 0.4.5-pre, etc...)
I'm getting the following error from the model binder when it tries to bind this version number field to the model field (string type):
{"The parameter conversion from type 'System.String' to type
'Models.NewFileVersion' failed because no type converter can convert
between these types."}
Specifically the error can be traced to our "VersionNumber" field - any ideas as to why this might be happening?
Edit: Source code below
NewFileVersion.cs
public class NewFileVersion
{
[Display(Name = "Version # (0.67, 0.66-pre, etc...)")]
[Required]
public string Version { get; set; }
[Required]
[StringLength(2000, ErrorMessage = "ChangeLog must be between 30 an 2000 characters", MinimumLength = 30)]
[Display(Name = "Version Notes (will be visible to end-users)")]
[DataType(DataType.MultilineText)]
public string ChangeLog { get; set; }
[Display(Name = "Target Platform")]
[UIHint("Enum")]
public FileType PlatformTarget { get; set; }
}
New.cshtml
#model ViewModels.NewFileVersion
#{
ViewBag.Title = "New";
}
<div class="container" id="main-content">
<div class="row">
<h2>
New</h2>
#using (Html.BeginForm("Create", "Files", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>NewFileVersion</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Version)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Version)
#Html.ValidationMessageFor(model => model.Version)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ChangeLog)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ChangeLog)
#Html.ValidationMessageFor(model => model.ChangeLog)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.PlatformTarget)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PlatformTarget)
</div>
<div class="editor-label">
<label for="">
File:</label></div>
<div class="editor-field">
<input type="file" name="fileData" required="required" /></div>
<p>
<input type="submit" value="Upload File" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
</div>
</div>
FilesController.cs
[HttpPost]
public ActionResult Create(NewFileVersion version, HttpPostedFileBase fileData)
{
//if our model is valid
if(ModelState.IsValid)
{
//etc....
}
ModelState.AddModelError("", "Invalid file submission");
return View("New", version);
}
Try renaming the version parameter for your Create action, e.g:
public ActionResult Create(NewFileVersion fileVersion, HttpPostedFileBase fileData) { ... }
The model binder may be getting confused between the string version model property and the NewFileVersion version action parameter.
You can see why this happens in the BindModel method, because the model has a property exactly matching the name of the action parameter it tries to bind as a simple type/model rather than a complex one.

Resources