Validator.TryValidateObject everytime return true. Why? I tried different field values.
Its my model:
public class CompanyPreviewMeta
{
[Required]
[Display(Name="Изображение")]
public string Image { get; set; }
[Required]
[Display(Name="Текст")]
[StringLength(100, MinimumLength = 20, ErrorMessage = "Значение {0} должно содержать от {2} до 100 символов.")]
public string Text { get; set; }
}
It's my validation, and "valid" is true everytime:
ValidationContext ValidatorContext = new ValidationContext(model, null, null);
List<ValidationResult> result = new List<ValidationResult>();
bool valid = Validator.TryValidateObject(model, ValidatorContext, result, true);
Related
Trying to use TryValidateProperty to validate Customer class, it fails validate on Address property which is another class. I'm getting this error
System.ArgumentException: 'The type 'Customer' does not contain a public property named 'Address.City'. (Parameter 'propertyName')'
public class Customer
{
[Required(ErrorMessage = "{0} is mandatory")]
[MaxLength(50, ErrorMessage = "The {0} can not have more than {1} characters")]
public string Name { get; set; }
[Required(ErrorMessage = "{0} is mandatory")]
[Range(0, 150, ErrorMessage = "The Age should be between 0 and 150 years")]
public int Age { get; set; }
[Required(ErrorMessage = "{0} is mandatory")]
public Address? Addresse { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
Validator.TryValidateProperty(this.Name,
new ValidationContext(this, null, null) { MemberName = "Name" },
results);
Validator.TryValidateProperty(this.Age,
new ValidationContext(this, null, null) { MemberName = "Age" },
results);
Validator.TryValidateProperty(this.Addresse.City, new ValidationContext(this, null, null) { MemberName = "Address.City" }, results);
return results;
}
}
var customer = new Customer()
{
//Name = "hello",
EntryDate = DateTime.Today,
Password = "AAAA",
PasswordConfirmation = "BBBB",
Age = -1,
Addresse = new Address()
{
//City = "CTG",
Street = "Goli"
}
};
var validationContext = new ValidationContext(customer);
var resultAdd = customer.Validate(validationContext);
How to validate the Address properties?
TIA.
I got it working making the following changes in Validate method. Need to pass the context correctly to the Validator.
public class Customer
{
[Required(ErrorMessage = "{0} is mandatory")]
[MaxLength(50, ErrorMessage = "The {0} can not have more than {1} characters")]
public string Name { get; set; }
[Required(ErrorMessage = "{0} is mandatory")]
[Range(0, 150, ErrorMessage = "The Age should be between 0 and 150 years")]
public int Age { get; set; }
[Required(ErrorMessage = "{0} is mandatory")]
public Address? Addresse { get; set; }
public IEnumerable<ValidationResult> Validate()
{
var results = new List<ValidationResult>();
Validator.TryValidateProperty(this.Name,
new ValidationContext(this, null, null) { MemberName = "Name" },
results);
Validator.TryValidateProperty(this.Age,
new ValidationContext(this, null, null) { MemberName = "Age" },
results);
Validator.TryValidateProperty(this.Addresse.City, new ValidationContext(this.Address, null, null) { MemberName = "City" }, results);
return results;
}
}
var customer = new Customer()
{
//Name = "hello",
EntryDate = DateTime.Today,
Password = "AAAA",
PasswordConfirmation = "BBBB",
Age = -1,
Addresse = new Address()
{
//City = "CTG",
Street = "Goli"
}
};
var result = customer.Validate();
Hope it helps someone.
good day ..
i created a model that has an property with [Notmapped] DataAnnotations and i created another class inherit from this model with same property but i add required DataAnnotations the problem is when i delete i got error "Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
"
My Model :
[Key]
[Display(AutoGenerateField = true, AutoGenerateFilter = true, Description = "IDDescription", GroupName = "IDGroupName", Name = "IDName", ShortName = "IDShortName", Prompt = "IDPrompt", Order = 50, ResourceType = typeof(Resources.BaseEntity))]
public long ID { get; set; }
[StringLength(207, ErrorMessageResourceName = "StringTooMuch", ErrorMessageResourceType = typeof(Resources.BaseSlider))]
[Required(AllowEmptyStrings = false, ErrorMessageResourceName = "DetailsRequired", ErrorMessageResourceType = typeof(Resources.BaseSlider))]
[Display(Name = "Description", ResourceType = typeof(Resources.BaseSlider))]
public string Description { get; set; }
[NotMapped]
public string ShortDescription
{
get
{
if (Description.Length <= 207)
{
return Description;
}
return Description.Substring(0, 207);
}
}
[Display(Name = "HasBTN", ResourceType = typeof(Resources.BaseSlider))]
public bool HasBTN { get; set; }
[Display(Name = "Is Image Dark")]
public bool IsDark { get; set; }
[Display(Name = "Link", ResourceType = typeof(Resources.BaseSlider))]
public string Link { get; set; }
[Display(Name ="Slider Type")]
public long SliderTypeID { get; set; }
[NotMapped]
//[ImageValidation(".jpg,.png,.japg", OriginalWidth = 1920, OriginalHeight = 600)]
[Display(AutoGenerateField = true, AutoGenerateFilter = true, Description = "ImagePathDescription", Name = "ImagePathName", ResourceType = typeof(Resources.BaseMore))]
public virtual HttpPostedFileBase ImagePathFile { get; set; }
#endregion
#region Relations
public virtual IList<BaseSliderPhotoUpload> Photos { get; set; }
public virtual BaseLookup SliderType { get; set; }
#endregion
public BaseSlider()
{
Photos = new List<BaseSliderPhotoUpload>();
}
and the class i created :
public class BaseSliderCreate : BaseSlider
{
#region Data
[NotMapped]
[Required]
//[ImageValidation(".jpg,.png,.japg", OriginalWidth = 1920, OriginalHeight = 600)]
[Display(AutoGenerateField = true, AutoGenerateFilter = true, Description = "ImagePathDescription", Name = "ImagePathName", ResourceType = typeof(Resources.BaseMore))]
public override HttpPostedFileBase ImagePathFile { get; set; }
#endregion
}
in delete actionresult code :
public ActionResult DeleteConfirmed(Guid id)
{
BaseSlider SliderObject = db.Sliders.Where(x => x.GUID == id && x.Deleted == null).FirstOrDefault();
SliderObject.Deleted = DateTime.Now;
SliderObject.DeletedByID = _CurrentUser.ID;
// Delete All Photos
DeletePhoto DeletePhoto = new DeletePhoto();
var DeletedPhotoName = new List<string>();
foreach (var name in SliderObject.Photos)
{
DeletedPhotoName.Add(name.FileName);
}
if (DeletePhoto.PhotoDeleted("Slider", DeletedPhotoName))
{
try
{
db.SliderPhotos.RemoveRange(SliderObject.Photos);
db.Entry(SliderObject).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception ex)
{
ErrorList.Add(ex.Message);
throw;
}
}
else
{
ErrorList.Add(DeletePhoto.ErrorMessage);
}
ViewBag.ErrorList = ErrorList;
return RedirectToAction("Delete", new { id = SliderObject.GUID });
}
when i save change i got error
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
i checked i found the EntityValidationErrors is that ImagePathFile is required..
thanks for helping my and i apologist for my bad English
I have a model defined this way:
public class AdvisoryViewModel : IValidatableObject
{
[Display(Name = "Start Date")]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true, ConvertEmptyStringToNull = true)]
public DateTime? StartDate { get; set; }
[Display(Name = "End Date")]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true, ConvertEmptyStringToNull = true)]
public DateTime? EndDate { get; set; }
[Display(Name = "Instructions")]
[Required(ErrorMessage = "Instructions are required")]
[MaxLength(500, ErrorMessage = "Instructions cannot be longer than 500 characters.")]
public string Instruction { get; set; }
IEnumerable<ValidationResult> IValidatableObject.Validate(ValidationContext validationContext)
{
List<ValidationResult> results = new List<ValidationResult>();
if (StartDate.HasValue &&
EndDate.HasValue &&
StartDate.Value > EndDate.Value)
{
ValidationResult result = new ValidationResult("Start date must be after end date.");
results.Add(result);
}
return results;
}
And I am validating it as follows:
var validationResults = new List<ValidationResult>();
if (!Validator.TryValidateObject(advisoryViewModel, new ValidationContext(advisoryViewModel), validationResults, true))
{
return Json(new { success = false, message = string.Join("; ", validationResults.Select(r => r.ErrorMessage)) });
}
What happens on validation is it first only calls the "Required" attributes - for example, if the start date is later than end date AND the instructions are null, it returns with only the message that instructions cannot be null. Once they are not null, it returns the start/end date error message.
Is there a way to have it do ALL of the validations up front rather than two attempts?
Also, is there a way the start/end validation can be added to client side results?
I am trying to remove the password captured in the parameters from my action filter and replace it with the word "Removed", in order for the parameters to be stored in the database for logging. The password is stored in a ViewModel (depending on the action). Below is sort of a "pseudo-code" as to what I am trying to achieve.
How would I go about masking/replacing the password to be saved in the database? The main issue I am having is that I do not know how to access the password parameter and change it. I have tried getting it using the actionParams.TryGetValue("model, out value) but the problem is that I do not know the type of value and it changes depending on the action. Also, I am unable to call many methods on actionParams["model"] (such as contains)
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var actionParam = filterContext.ActionParameters;
// Remove the password from the parameters
if (actionParam.ContainsKey("model") && actionParam["model"] != null)
{
// If actionParam["model"].ToLower().Contains("password")
// actionParam["model"]["password"] = "Removed";
// If actionParam["model"].ToLower().Contains("confirm password")
// actionParam["model"]["confirm password"] = "Removed";
}
string str = Json.Encode(filterContext.ActionParameters).Trim();
string par = string.Empty;
if (str.Length > 2)
{
par = str.Substring(1, str.Length - 2).Replace("\"", string.Empty);
}
ActionLog log = new ActionLog()
{
SessionId = filterContext.HttpContext.Session.SessionID,
UserName = (request.IsAuthenticated) ? filterContext.HttpContext.User.Identity.Name : "Anonymous",
Controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
Action = filterContext.ActionDescriptor.ActionName,
ActionParameters = par,
IsPost = request.HttpMethod.ToLower() == "post" ? true : false,
IPAddress = request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? request.UserHostAddress,
UserAgent = request.UserAgent,
ActionDate = filterContext.HttpContext.Timestamp
};
//Store the Audit into the Database
ActionLogContext context = new ActionLogContext();
context.ActionLogs.Add(log);
context.SaveChanges();
// Finishes executing the Action as normal
base.OnActionExecuting(filterContext);
}
Example of possible view models
public class LoginViewModel
{
[Required]
[Display(Name = "User ID")]
[RegularExpression("^[a-zA-Z0-9]+$", ErrorMessage="Letters and Numbers Only")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
}
public class ResetPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[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; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
Example of possible action parameters
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
One approach would be to use an interface as an abstraction so you are not dealing directly with a ViewModel. First, create some interfaces for the action filter to interact with.
public interface IPassword
{
string Password { get; set; }
}
public interface IConfirmPassword
{
string ConfirmPassword { get; set; }
}
Next, make your ViewModel classes implement those interfaces.
public class LoginViewModel : IPassword
{
[Required]
[Display(Name = "User ID")]
[RegularExpression("^[a-zA-Z0-9]+$", ErrorMessage = "Letters and Numbers Only")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
}
public class ResetPasswordViewModel : IPassword, IConfirmPassword
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[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; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
Then it is just a matter of updating your filter code. The filter doesn't need to know anything more about your model other than the fact that it implements IPassword or IConfirmPassword, which it can check with a cast.
Of course, for it to work correctly, you have to restore the original values before executing the action method (or alternatively do the logging after the action is run) so the action method will have the correct values.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var actionParam = filterContext.ActionParameters;
IPassword password = null;
IConfirmPassword confirmPassword = null;
string originalPassword;
string originalConfirmPassword;
// Remove the password from the parameters
if (actionParam.ContainsKey("model") && actionParam["model"] != null)
{
// If the model doesn't implement the interface, the result
// here will be null.
password = actionParam["model"] as IPassword;
confirmPassword = actionParam["model"] as IConfirmPassword;
}
if (password != null)
{
// Store the original value so it can be restored later
originalPassword = password.Password;
password.Password = "Removed";
}
if (confirmPassword != null)
{
// Store the original value so it can be restored later
originalConfirmPassword = confirmPassword.ConfirmPassword;
confirmPassword.ConfirmPassword = "Removed";
}
string str = Json.Encode(filterContext.ActionParameters).Trim();
string par = string.Empty;
if (str.Length > 2)
{
par = str.Substring(1, str.Length - 2).Replace("\"", string.Empty);
}
ActionLog log = new ActionLog()
{
SessionId = filterContext.HttpContext.Session.SessionID,
UserName = (request.IsAuthenticated) ? filterContext.HttpContext.User.Identity.Name : "Anonymous",
Controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
Action = filterContext.ActionDescriptor.ActionName,
ActionParameters = par,
IsPost = request.HttpMethod.ToLower() == "post" ? true : false,
IPAddress = request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? request.UserHostAddress,
UserAgent = request.UserAgent,
ActionDate = filterContext.HttpContext.Timestamp
};
//Store the Audit into the Database
ActionLogContext context = new ActionLogContext();
context.ActionLogs.Add(log);
context.SaveChanges();
// Restore the original values
if (password != null)
{
password.Password = originalPassword;
}
if (confirmPassword != null)
{
confirmPassword.ConfirmPassword = originalConfirmPassword;
}
// Finishes executing the Action as normal
base.OnActionExecuting(filterContext);
}
During seeding in MVC how can I prevent GUIDs being generated in the database?
This is my class:
[Table("Languages")]
public class Language
{
[Key]
public Guid ID { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 0)]
public string Name { get; set; }
[Required]
[StringLength(10, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 0)]
public string Code { get; set; }
[DefaultValue(true)]
public bool IsEnabled { get; set; }
}
And this is my seed method:
protected override void Seed(SunLite.Models.SunLiteDBContext context)
{
public static SunLiteDBContext Run(SunLiteDBContext context)
{
context.Languages.AddOrUpdate
(
x => x.ID,
new Language { Name = "English", Code = "en-gb", ID = Guid.Parse("{fab5422a-f63d-4042-b7b1-705f69854bc9}"), IsEnabled = true },
new Language { Name = "Spanish", Code = "es", ID = Guid.Parse("{24380e18-02bf-4668-87ee-b2514d17f384} "), IsEnabled = true },
new Language { Name = "German", Code = "de", ID = Guid.Parse("{2c05c682-704f-43a4-864b-742ae359aa30}"), IsEnabled = true },
new Language { Name = "French", Code = "fr", ID = Guid.Parse("{bf929fe3-67a0-425c-81d2-1ef554affc7c} "), IsEnabled = true },
new Language { Name = "Turkish", Code = "tr", ID = Guid.Parse("{9d09cb45-3d0d-4238-a903-96c4f17481a1} "), IsEnabled = true }
);
}
}
At the moment this data is added to the database each time with new guids, not the ones I specified.
You can add attribute [DatabaseGenerated(DatabaseGeneratedOption.None)] to your ID property. It should help.