custom validator in asp.net mvc3 - asp.net-mvc-3

I have created a custom validator in my asp.net mvc3 application like this:
{
if (customerToValidate.FirstName == customerToValidate.LastName)
return new ValidationResult("First Name and Last Name can not be same.");
return ValidationResult.Success;
}
public static ValidationResult ValidateFirstName(string firstName, ValidationContext context)
{
if (firstName == "Nadeem")
{
return new ValidationResult("First Name can not be Nadeem", new List<string> { "FirstName" });
}
return ValidationResult.Success;
}
and I have decorated my model like this:
[CustomValidation(typeof(CustomerValidator), "ValidateCustomer")]
public class Customer
{
public int Id { get; set; }
[CustomValidation(typeof(CustomerValidator), "ValidateFirstName")]
public string FirstName { get; set; }
public string LastName { get; set; }
}
my view is like this:
#model CustomvalidatorSample.Models.Customer
#{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
#using (#Html.BeginForm())
{
#Html.ValidationSummary(false)
<div class="editor-label">
#Html.LabelFor(model => model.FirstName, "First Name")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LastName, "Last Name")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LastName)
</div>
<div>
<input type="submit" value="Validate" />
</div>
}
But validation doesn't fire. Please suggest solution.
Thanks

How do you know the validation doesn't fire? Are you setting a break point in your controller?
You are not displaying any validation errors in your view. You need to add the following lines to the view.
#Html.ValidationMessageFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.LastName)
You will want to remove the custom validation from the class. Leave it on the properties though.

Related

Can I restrict client-side validation to specific fields?

I have a Form where I successfully use unobtrusive-validation with the [remote] annotation.
I have other fields in the Form with [required] annotation in the model but I don't want client-side validation for these fields.
I only want server-side validation for [required] fields
I haven't found a solution and I wonder if it's easily feasible?
EDIT :
A part of my code
Part of my model :
I would like the first field : "Email" use Unobtrusive-validation and the second one : "PasswordHash" only use server-side validation even if it has [required] annotation.
Finally, i don't want an AJAX error message for all my form 's fields.
[Required(ErrorMessageResourceType = typeof(Project.Resources.Models.Accounts.User), ErrorMessageResourceName = "EMAIL_REQUIRED")]
[Display(Name = "EMAIL_DISPLAY", ResourceType = typeof(Project.Resources.Models.Accounts.User))]
[Remote("EmailExists", "User", "An account with this email address already exists.")]
public string Email { get; set; }
[Required(ErrorMessageResourceType = typeof(Project.Resources.Models.Accounts.User), ErrorMessageResourceName = "PASSWORDHASH_REQUIRED")]
[DataType(DataType.Password)]
[Display(Name = "PASSWORDHASH_DISPLAY", ResourceType = typeof(Project.Resources.Models.Accounts.User))]
public string PasswordHash { get; set; }
Part of the action in Controller
Server-side validation :
[HttpPost]
public ActionResult Register(User user)
{
if (ModelState.IsValid )
{
DataContext.User.Add(user);
DataContext.SaveChanges();
return RedirectToAction("Index");
}
return View(user);
}
Ajax validation :
public JsonResult EmailExists(string email)
{
User user = DataContext.User.SingleOrDefault(x => x.Email == email);
return user == null ?
Json(true, JsonRequestBehavior.AllowGet) :
Json(
string.Format("an account for address {0} already exists.",
email), JsonRequestBehavior.AllowGet);
}
Part of the view :
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.PasswordHash)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PasswordHash)
#Html.ValidationMessageFor(model => model.PasswordHash)
</div>
<p>
<input type="submit" name="op" id="edit-submit" value="#Project.Resources.Views.User.Register.SUBMIT_FORM" class="form-submit art-button" />
</p>
}
When I click on the sumbit button i would like a server-side validation.
And for some specific fields like Email i would like an Ajax Validation.
there might be better ways, but one way to accomplish this is to do the following
#using (Html.BeginForm("Register", "Home", FormMethod.Post, new { id = "registerForm" }))
{
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.PasswordHash)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PasswordHash)
#Html.ValidationMessageFor(model => model.PasswordHash)
</div>
<p>
<input type="submit" name="op" id="edit-submit" value="#Project.Resources.Views.User.Register.SUBMIT_FORM" class="form-submit art-button" />
</p>
}
<script type="text/javascript">
// add the rules for the fields that you want client-side validation on
// leave out the fields that you dont want client-side validation. they will be validated
// on server side.
$("#registerForm").validate({
rules: {
Email: { required: true }
},
messages: {
Email: { required : "Email is required" }
}
});
</script>

How to use IValidatableObject on Partial View

I can't seem to figure out how to validate the pieces of a partial view for an ViewModel that has the partial ViewModel as a child object. Here's my lowest level piece, which will ALWAYS be consumed as a partial view inside other form tags:
namespace MVC3App.ViewModels
{
public class Payment : IValidatableObject
{
public decimal Amount { get; set; }
public int CreditCardNumber { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Amount < 20)
yield return new ValidationResult("Please pay more than $20", new string[] { "Amount" });
}
}
}
And here's the 'main' ViewModel that includes it:
namespace MVC3App.ViewModels
{
public class NewCustomerWithPayment :IValidatableObject
{
public string Name { get; set; }
public int Age { get; set; }
public ViewModels.Payment PaymentInfo { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Age < 18)
yield return new ValidationResult("Too young.", new string[] { "Age" });
}
}
}
For the View of the NewCustomerWithPayment, I have this:
#model MVC3App.ViewModels.NewCustomerWithPayment
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>NewCustomerWithPayment</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Age)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Age)
#Html.ValidationMessageFor(model => model.Age)
</div>
</fieldset>
#Html.Partial("Payment")
<p><input type="submit" value="Create" /></p>
}
And the Partial View "Payment" is ALWAYS rendered inside another Html.Beginform tag, it just has this:
#model MVC3App.ViewModels.Payment
<h2>Payment</h2>
<fieldset>
<legend>Payment</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Amount)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Amount)
#Html.ValidationMessageFor(model => model.Amount)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CreditCardNumber)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CreditCardNumber)
#Html.ValidationMessageFor(model => model.CreditCardNumber)
</div>
</fieldset>
My problem is that I cannot get the Validation on the 'Payment' viewmodel to work. Can anyone with experience using IValidatableObject on ViewModels which are rendered as Partial Views chime in and give me a validation pattern that works? I can live without JavaScript validation if I have to.
These answers all have some great info, but my immediate issue was resolved by using this:
#Html.EditorFor(model => model.PaymentInfo)
Instead of this:
Html.Partial("Payment", Model.PaymentInfo)
I was surprised that this worked, but it does. The EditorFor helper renders out the partial view just like Html.Partial, and wires in the validation automatically. For some reason, it does call the validation twice on the child model (Payment in my example), which seems to be a reported issue for some other people (http://mvcextensions.codeplex.com/workitem/10), so I have to include a boolean for 'HasBeenValidated' on each model and check for it at the beginning of the Validate call.
Update: you must move your view to the EditorTemplates folder under /Views/Shared/ in order for the view to be used by the EditorFor helper. Otherwise, the EditorFor will give you the default editing fields for the types.
Here is a lame example of a custom validator for a checkbox :) I would write a custom validator or use a regex maybe. This may get you on the right path and be easier.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class CheckBoxMustBeTrueAttribute : ValidationAttribute, IClientValidatable
{
#region IClientValidatable Members
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,
ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredcheckbox"
};
}
#endregion
public override bool IsValid(object value)
{
if (value is bool)
{
return (bool) value;
}
return true;
}
}
Most probably IValidatableObject is recognized only on the root model. You can call the inner model Validate method from the root model:
public class NewCustomerWithPayment :IValidatableObject {
...
public ViewModels.Payment PaymentInfo { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Age < 18)
yield return new ValidationResult("Too young.", new string[] { "Age" });
if (this.PaymentInfo != null)
yield return this.PaymentInfo.Validate(validationContext);
}
}
Note: Not sure if the above compiles.

complex model and unique id for each item of list properties in mvc

I am developing an asp.net mvc 3.0 and using complex model in a view as below :
#model StoresAndMalls.DataModel.Entities.User
........
<div class="editor-label">
#Html.LabelFor(model => model.EmailAddress)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.EmailAddress)
#Html.ValidationMessageFor(model => model.EmailAddress)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Status)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Status)
#Html.ValidationMessageFor(model => model.Status)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Role)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Role)
#Html.ValidationMessageFor(model => model.Role)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.UserRules)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserRules,"UserRules", new { AllRules = ViewBag.AllRules})
</div>
Here are my models :
public partial class User
{
......
public virtual ICollection<UserRule> UserRules { get; set; }
public virtual Role Role { get; set; }
}
public class UserRule
{
.....
[ForeignKey("UserId")]
public virtual User User { get; set; }
[ForeignKey("RuleId")]
public virtual Rule Rule { get; set; }
}
public partial class Role
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
In recent projects, when I created a view with complex model, the form elements, such as Role, where prefixed with User, I mean there were like 'User.Role', but now they are not, and even though I use editor template for 'UserRules' property of User class, it also does not work as well and do not have 'User' prefix with,
here are my editor views for Role and UserRule:
#model IEnumerable<StoresAndMalls.DataModel.Entities.UserRule>
#{
var allrules = (ViewData["AllRules"] as List<StoresAndMalls.DataModel.Entities.Rule>);
int c = 0;
}
#
foreach (var item in allrules)
{
#item.Description
#Html.CheckBox(Model.Any(x => x.RuleId == item.Id))
c++;
<br />
}
-----------
#model StoresAndMalls.DataModel.Entities.Role
#{
var selecetList = (ViewData["Roles"] as List<StoresAndMalls.DataModel.Entities.Role>).
ConvertAll(x => new SelectListItem { Value = x.Id.ToString(), Text = x.Name, Selected = x.Id == Model.Id });
}
#Html.DropDownListFor(x => x, selecetList)
-------------
For 'UserRule' I tried
#model StoresAndMalls.DataModel.Entities.UserRule
But it throws :
The model item passed into the dictionary is of type 'System.Collections.Generic.HashSet`1[DataModel.Entities.UserRule]', but this dictionary requires a model item of type 'DataModel.Entities.UserRule'.
Edited :
public ActionResult UpdateManager(Guid id)
{
ViewBag.Roles = unitofwork.RoleRepository.Get();
ViewBag.AllRules = unitofwork.UserRepository.GetByID(id).UserRules;
var model = unitofwork.UserRepository.GetByID(id);
return View(model);
}

Uploading and converting an image/file into a mssql databse

I would like to save an image/file into a sql-database in an asp.net mvc3 project. I found some examples where they have to save the image into a database with nothing else. When I want to add a "Inzending" instance in my database with the "Create" methode from my Controller, I also need to put the file into an byte array. I have to insert all the information into my database at the same time, because no field may be null. I hope someone can help me with this, I'm just starting with it in school and this is the first real big problem i have to solve ( i find it quite hard to solve). some of my code I already wrote:
Controller:
[HttpPost]
public ActionResult Create(INZENDING inzending)
{
string user = User.Identity.Name;
var users = db.aspnet_Users.Single(p => p.UserName == user).UserId;
inzending.Userid = users;
int id = db.INZENDINGs.Count() + 1;
inzending.InzendingNr = id;
if (ModelState.IsValid)
{
db.INZENDINGs.Add(inzending);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ErfgoedNr = new SelectList(db.ERFGOEDFICHEs, "ErfgoedNr", "Naam", inzending.ErfgoedNr);
ViewBag.Aard = new SelectList(db.INPUT_AARD, "Aard", "Aard", inzending.Aard);
ViewBag.Type = new SelectList(db.INPUTTYPEs, "type", "type", inzending.Type);
ViewBag.Status = new SelectList(db.STATUS, "Waarde", "Waarde", inzending.Status);
return View(inzending);
}
view :
#model Erfgoed.Models.INZENDING
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>INZENDING</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Titel)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Titel)
#Html.ValidationMessageFor(model => model.Titel)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Auteur)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Auteur)
#Html.ValidationMessageFor(model => model.Auteur)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Datum)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Datum)
#Html.ValidationMessageFor(model => model.Datum)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.bestandsgrootte)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.bestandsgrootte)
#Html.ValidationMessageFor(model => model.bestandsgrootte)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Type, "INPUTTYPE")
</div>
<div class="editor-field">
#Html.DropDownList("Type", String.Empty)
#Html.ValidationMessageFor(model => model.Type)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Aard, "INPUT_AARD")
</div>
<div class="editor-field">
#Html.DropDownList("Aard", String.Empty)
#Html.ValidationMessageFor(model => model.Aard)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ErfgoedNr, "ERFGOEDFICHE")
</div>
<div class="editor-field">
#Html.DropDownList("ErfgoedNr", String.Empty)
#Html.ValidationMessageFor(model => model.ErfgoedNr)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.bestand, "Bestand");
</div>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Model:
namespace Erfgoed.Models
{
using System;
using System.Collections.Generic;
public partial class INZENDING
{
public int InzendingNr { get; set; }
public string Titel { get; set; }
public string Auteur { get; set; }
public System.DateTime Datum { get; set; }
public string bestandsgrootte { get; set; }
public string Type { get; set; }
public string Aard { get; set; }
public byte[] bestand { get; set; }
public System.Guid Userid { get; set; }
public int ErfgoedNr { get; set; }
public string Status { get; set; }
public virtual aspnet_Membership aspnet_Membership { get; set; }
public virtual ERFGOEDFICHE ERFGOEDFICHE { get; set; }
public virtual INPUT_AARD INPUT_AARD { get; set; }
public virtual INPUTTYPE INPUTTYPE { get; set; }
public virtual STATUS STATUS1 { get; set; }
}
}
[HttpPost]
public ActionResult Create(HttpPostedFileBase file, FormCollection collection)
{
var attachment = new Attachment();
if(TryUpdateModel(attachment))
{
long length =
file.InputStream.Length;
attachment.Data = new byte[length];
file.InputStream.Read(attachment.Data, 0, (int)length);
attachmentRepository.Add(attachment);
attachmentRepository.Save();
return RedirectToAction("Index");
}
return null;
}
Here's how i'm saving file in ms sql database. Sorry if i wrongly understood your question

mvc3 razor dropdownlistfor causing update model to fail

I have a problem with the following code. Basically when I call updatemodel it fails but I dont get an inner exception. If i change the drop down to a text box it works.
class:
public class Member
{
[Key]
public int MemberID { get; set; }
[StringLength(50),Required()]
public string Name { get; set; }
[Range(0,120)]
public short Age { get; set; }
[Required(),MaxLength(1)]
public string Gender {get; set;}
[Required()]
public virtual Team Team { get; set; }
}
controller methods:
[HttpGet]
public ActionResult ViewMember(int id)
{
try
{
var m = db.GetMember(id);
var maleItem = new SelectListItem{Text = "Male", Value= "M", Selected=m.Gender == "M"};
var femaleItem = new SelectListItem{Text = "Female", Value="F", Selected=m.Gender == "F"};
List<SelectListItem> items = new List<SelectListItem>();
items.Add(maleItem);
items.Add(femaleItem);
var genders = new SelectList(items, "Value", "Text");
ViewBag.Genders = genders;
return View(m);
}
catch (Exception ex)
{
return View();
}
}
[HttpPost]
public ActionResult ViewMember(Member m)
{
try
{
var member = db.GetMember(m.MemberID);
m.Team = db.GetTeam(member.Team.TeamID);
UpdateModel(member);
db.Save();
return RedirectToAction("ViewMembers", new { id = member.Team.TeamID });
}
catch (Exception ex)
{
return View();
}
}
and finally cshtml code:
#model GreenpowerAdmin.Models.Member
#using GreenpowerAdmin.Helpers
#{
ViewBag.Title = "ViewMember";
}
<h2>ViewMember</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Member</legend>
#Html.HiddenFor(model => model.MemberID)
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Team.Name)
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.Team.Name)
#Html.ValidationMessageFor(model => model.Team.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Age)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Age)
#Html.ValidationMessageFor(model => model.Age)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Gender)
</div>
<div class="editor-field">
#Html.DropDownList("Gender", ViewBag.Genders as SelectList)
#Html.ValidationMessageFor(model => model.Gender)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "ViewMembers", new { id=Model.Team.TeamID })
</div>
When I click the save button the updatemodel line fails. As said above, it doesn't fail if I use a text box for gender. Does anyone know what is making it fail or how to debug it?
Try declaring the MemberID property as a nullable integer:
[Key]
public int? MemberID { get; set; }

Resources