Based On this Article: Manual Validation with Data Annotations
I write this code:
public class Person04
{
[CustomValidation(typeof(AWValidation), "ValidateSalesAmount")]
public int SalesAmout { get; set; }
[DataType(DataType.EmailAddress, ErrorMessage = "Invalid E-mail")]
public string EmailAddress { get; set; }
[Range(0, 99, ErrorMessage = "Age should be in range 0 to 99")]
public int Age { get; set; }
[Required(ErrorMessage="Name is required")]
public string Name { get; set; }
[StringLength(10, ErrorMessage = "Invalid Last Name")]
public string LastName { get; set; }
}
public class AWValidation
{
public static ValidationResult ValidateSalesAmount(int salesAmount)
{
if (salesAmount < 0)
{
return new ValidationResult("Invalid Sales Amount");
}
return ValidationResult.Success;
}
}
and
var person = new Person04() { SalesAmout = -1, Age = 100, EmailAddress = "nima", LastName = "Arian The Great" };
var context = new ValidationContext(person, serviceProvider: null, items: null);
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(person, context, results);
if (!isValid)
{
foreach (var validationResult in results)
{
Console.WriteLine(validationResult.ErrorMessage);
}
}
but this Code just write 1 error:
Name is required
Why other errors not specified?
thanks
var isValid = Validator.TryValidateObject(person, context, results, true);
You were missing the last boolean parameter which indicates that you want all properties to be validated:
validateAllProperties: true to validate all properties; if false, only required attributes are validated.
I think i found solution.You just missed an object of class ValidationContext.
public class AWValidation
{
public static ValidationResult ValidateSalesAmount(int salesAmount,ValidationContext validationContext)
{
if (salesAmount < 0)
{
return new ValidationResult("Invalid Sales Amount");
}
return ValidationResult.Success;
}
}
Related
I need to create a Create Custom Return Validate in Asp Core 2.2 in WebApi.
First Step :
I Created on OnResultExecuting :
public override void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is BadRequestObjectResult badRequestObjectResult)
{
var message = badRequestObjectResult.Value.ToString();
if (badRequestObjectResult.Value is SerializableError errors)
{//The Code skips the if statement. Why?
var errorMessages = errors.SelectMany(p => (string[])p.Value).Distinct();
message = string.Join(" | ", errorMessages);
}
context.Result = new JsonResult(new ReturnResult(false, ResultStatus.BadRequest, message))
{ StatusCode = badRequestObjectResult.StatusCode };
}
}
The Code skips the if statement. Why?
Second Step :
Created a IValidatableObject in UserDto :
public class UserDto : IValidatableObject
{
[Required]
public string Name { get; set; }
[Required]
public string Family { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string Username { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string Phone { get; set; }
[Required]
public GenderType Gender { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
foreach (var item in ValidateList.UsernameBanList)
if (Username.Equals(item, StringComparison.OrdinalIgnoreCase))
yield return new ValidationResult("Username Invalid", new[] { nameof(Username) });
foreach (var item in ValidateList.PasswordBanList)
if (Password.Equals(item, StringComparison.OrdinalIgnoreCase))
yield return new ValidationResult("Password Invalid, new[] { nameof(Password) });
foreach (var item in ValidateList.EmailBanList)
if (Email.Equals(item, StringComparison.OrdinalIgnoreCase))
yield return new ValidationResult("Email Invalid", new[] { nameof(Email) });
}
}
Third Step :
I Create a ReturnResult Class :
public class ReturnResult
{
public bool Success { get; }
public ResultStatus Status { get; }
public string Message { get; }
public ReturnResult(bool Success, ResultStatus Status, string Message = null)
{
this.Success = Success;
this.Status = Status;
this.Message = Message ?? Status.ToDisplay();
}
#region implicit operator
public static implicit operator ReturnResult(OkResult result)
{
return new ReturnResult(true, ResultStatus.Success);
}
public static implicit operator ReturnResult(BadRequestResult result)
{
return new ReturnResult(false, ResultStatus.BadRequest);
}
public static implicit operator ReturnResult(BadRequestObjectResult result)
{
var message = result.ToString();
if (result.Value is SerializableError error)
{
var errorMessage = error.SelectMany(p => (string[])p.Value).Distinct();
message = string.Join(" | ", errorMessage);
}
return new ReturnResult(false, ResultStatus.BadRequest, message);
}
public static implicit operator ReturnResult(ContentResult result)
{
return new ReturnResult(true, ResultStatus.Success, result.Content);
}
public static implicit operator ReturnResult(NotFoundResult result)
{
return new ReturnResult(false, ResultStatus.NotFound);
}
#endregion
}
Now All Return in Api by this Format :
{
"success": true,
"status": 0,
"message": "success process"
}
In UserDto i Create a Validate in for Username and Password and Email then i need to return all error of them return by this format :
{
"success": true,
"status": 0,
"message": "Email Invalid | Password Invalid | Username Invalid"
}
But it not show me by this format , it show me this format :
{
"success": false,
"status": 2,
"message": "Microsoft.AspNetCore.Mvc.ValidationProblemDetails"
}
Now how can I solve this problem ???? I found This problem here but doesn't explain why the code doesn't work?
I'm using MVVM Light in Visual Studio 2015 to build an WPF app. There's a method in the code that's repeated with slight variation 4 times in the code; the only difference is the type of the ObservableCollection being modified and the method called on the data service layer.
Here's the method, which returns an ObservableCollection of StatusViewModel objects, which are used to populate a ComboBox; the StatusVm is used for binding to the SelectedItem of the ComboBox, is set as the first item in the collection and is "blank":
private async Task<ObservableCollection<StatusViewModel>> GetStatuses()
{
var result = new ObservableCollection<StatusViewModel>();
var blank = new StatusViewModel
{
StatusId = -1,
Status = null,
Description = null,
IsActive = false,
CreatedDate = DateTime.Now
};
result.Add(blank);
var dataService = new MyDataService();
foreach (var c in await dataService.GetStatuses())
result.Add(c);
StatusVm =
result.SingleOrDefault(c => c.StatusId.Equals(-1));
return result;
}
Here's the private field and public property for StatusVm:
private StatusViewModel _statusVm;
public StatusViewModel StatusVm
{
get { return _statusVm; }
set
{
if (Equals(value, _statusVm)) return;
_statusVm = value;
RaisePropertyChanged();
}
}
Now imagine the above being repeated 3 more times, with 3 more VM types! How do I make GetStatuses() into a method that can take different view model types and call the appropriate method on the data service? Thank you.
Update: Here are the property and method for another of the types:
private MroViewModel_mroVm;
public MroViewModel MroVm
{
get { return _mroVm; }
set
{
if (Equals(value, _mroVm)) return;
_mroVm = value;
RaisePropertyChanged();
}
}
private async Task<ObservableCollection<MroViewModel>> GetMro()
{
var result = new ObservableCollection<MroViewModel>();
var blank = new MroViewModel
{
StatusId = -1,
Status = null,
Description = null,
IsActive = false,
CreatedDate = DateTime.Now
};
result.Add(blank);
var dataService = new MyDataService();
foreach (var c in await dataService.GetMro())
result.Add(c);
MroVm =
result.SingleOrDefault(c => c.StatusId.Equals(-1));
return result;
}
You would create interface for common properties.
internal interface IStatusViewModel {
int StatusId { get; set; }
string Status { get; set; }
string Description { get; set; }
bool IsActive { get; set; }
DateTime CreatedDate { get; set; }
}
Implement the interface in classes that you need to retrieve status for
internal class MroViewModel : IStatusViewModel {
public int StatusId { get; set; }
public string Status { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedDate { get; set; }
}
Make method static and pass the service function which will call appropriate method to retrieve old statuses.
public static async Task<ObservableCollection<T>> GetStatuses<T>(
Func<MyDataService, Task<IEnumerable<T>>> retrieveStatusesAction)
where T : IStatusViewModel, new()
{
var result = new ObservableCollection<T>();
var blank = new T
{
StatusId = -1,
Status = null,
Description = null,
IsActive = false,
CreatedDate = DateTime.Now
};
result.Add(blank);
var dataService = new MyDataService();
foreach (var c in await retrieveStatusesAction(dataService))
result.Add(c);
// TODO Implement Expression<Func<TSource, TResult>> projection for assigning to VM
StatusVm = result.SingleOrDefault(c => c.StatusId.Equals(-1));
return result;
}
You would then call this method like so:
GetStatuses((service) => service.GetMro());
I didn't test this and the StatusVm needs to be assigned using expression compilation. I will take a look at how to do that now, but the idea is there.
For the Expression and property assigning:
Property selector Expression<Func<T>>. How to get/set value to selected property
-- EDIT --
Something like this for VM assignment:
public static async Task<ObservableCollection<T>> GetStatuses<T, TContainer>(
TContainer instance,
Expression<Func<TContainer, T>> viewModelProjection,
Func<MyDataService, Task<IEnumerable<T>>> retrieveStatusesAction)
where T : IStatusViewModel, new()
{
var result = new ObservableCollection<T>();
var blank = new T
{
StatusId = -1,
Status = null,
Description = null,
IsActive = false,
CreatedDate = DateTime.Now
};
result.Add(blank);
var dataService = new MyDataService();
foreach (var c in await retrieveStatusesAction(dataService))
result.Add(c);
var vmStatus = result.SingleOrDefault(c => c.StatusId.Equals(-1));
// Warning: Check casted values, this is unsafe
var vm = (PropertyInfo)((MemberExpression)viewModelProjection.Body).Member;
vm.SetValue(instance, vmStatus, null);
return result;
}
You would then call the method like this:
await GetStatuses(this, inst => inst.MroVm, (service) => service.GetMro());
If you are not familiar with expressions, I will explain.
First argument is object in which view model instance is located. Second argument selects the property from that object that corresponds to the view model that needs to be changed. Last argument is function that takes the service and returns appropriate method that retrieves the statuses - this is like pointer to the function in C++.
This will compile, but not sure if it will behave as expected. It should. If you have any problems, please write them down in comments.
You could define interfaces and try a combo of Strategy and Factory, like the following (I skipped async/await for simplicity):
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting");
var mainVm = new MainViewModel();
mainVm.GetStatuses();
mainVm.GetMro();
Console.WriteLine("Status: {0} {1}", mainVm.StatusVm.Name, mainVm.StatusVm.CreateDate);
Console.WriteLine("MroVm: {0} {1}", mainVm.MroVm.Name, mainVm.MroVm.CreateDate);
}
}
public class MainViewModel
{
public StatusViewModel StatusVm { get; set; }
public MroViewModel MroVm { get; set; }
public void GetStatuses()
{
var result = Get(VmKind.Status);
StatusVm = result.SingleOrDefault(c => c.StatusId.Equals(-1)) as StatusViewModel;
}
public void GetMro()
{
var result = Get(VmKind.Mro);
MroVm = result.SingleOrDefault(c => c.StatusId.Equals(-1)) as MroViewModel;
}
public IEnumerable<IVm> Get(VmKind vmKind)
{
var dataService = new MyDataService();
return dataService.Get(vmKind);
}
}
public interface IVm
{
int StatusId { get; set; }
DateTime CreateDate { get; set; }
string Name { get; }
}
public class StatusViewModel : IVm
{
public DateTime CreateDate { get; set; }
public int StatusId { get; set; }
public string Name { get { return "StatusViewModel"; } }
}
public class MroViewModel : IVm
{
public DateTime CreateDate { get; set; }
public int StatusId { get; set; }
public string Name { get { return "MroViewModel"; } }
}
public enum VmKind {Status, Mro }
#region Strategy
public interface IDataGetter
{
IEnumerable<IVm> Get(VmKind vmKind);
}
public class MyDataService : IDataGetter {
public IEnumerable<IVm> Get(VmKind vmKind)
{
switch (vmKind)
{
case VmKind.Status:
return GetStatuses();
//break;
case VmKind.Mro:
return GetMro();
//break;
default:
throw new ArgumentException("Unknown VM type");
}
}
private IEnumerable<IVm> GetMro()
{
return new List<MroViewModel> {
new MroViewModel { StatusId = -1, CreateDate = DateTime.Now },
new MroViewModel { StatusId = 2, CreateDate = DateTime.Now }
};
}
private IEnumerable<StatusViewModel> GetStatuses()
{
return new List<StatusViewModel> {
new StatusViewModel { StatusId = -1, CreateDate = DateTime.Now },
new StatusViewModel { StatusId = 2, CreateDate = DateTime.Now }
};
}
}
#endregion
#region Factory
public class VmFactory {
static IVm Create(VmKind vmKind)
{
IVm result = null;
switch (vmKind)
{
case VmKind.Status:
result = new StatusViewModel { StatusId = -1, CreateDate = DateTime.Now };
break;
case VmKind.Mro:
result = new MroViewModel { StatusId = -1, CreateDate = DateTime.Now };
break;
default:
throw new ArgumentException("Unknown VM type");
//break;
}
return result;
}
}
#endregion
I didn't actually use the Factory here, but you could do it for easy VM creation.
I have a tricky requirement where i have to compare two model properties which are of custom class types. Server side validation is straight forward but achieving client side validation using jquery unobtrusive is not clear . When the mvc renders the html i am not seeing data-val attributes appended
public class CommunicationViewModel
{
public PhoneViewModel MobilePhone { get; set; }
[ComparePhoneLocalized("CommunicationView.MobilePhone")]
public PhoneViewModel ConfirmMobilePhoneNumber { get; set; }
........
........
}
public class PhoneViewModel
{
public string Area { get; set; }
public string Number { get; set; }
public string Extension { get; set; }
public string CountryCode { get; set; }
}
Custom validator
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class CompareConditionalLocalizedAttribute : CompareAttribute, IClientValidatable
{
private readonly object _typeId = new object();
private const string ErrorMsg = "Rentered mobile number does not match the original";
public string FieldName { get; set; }
//public MessageManagement.MessageContext MessageContext { get; set; }
//public MessageCode MessageCode { get; set; }
public new string OtherProperty { get; set; }
public CompareConditionalLocalizedAttribute(string otherProperty)
: base(otherProperty)
{
OtherProperty = otherProperty;
}
public override string FormatErrorMessage(string name)
{
//** This error message will eventually be driven by resource provider factory **//
return ErrorMsg;
//var msg = Message.Create(MessageCode, MessageStatusType.Error, FieldName);
//var messageRepository = ServeContext.Current.ResolveInstance<IMessageRepository>();
//msg = messageRepository.MapMessage(msg, MessageContext);
//return msg.MessageText;
}
protected override ValidationResult IsValid(object value, ValidationContext
validationContext)
{
if (String.IsNullOrEmpty(OtherProperty)) return null;
var otherProperty = validationContext.ObjectType.GetProperty(OtherProperty);
var compareTo = (string)otherProperty.GetValue(validationContext.ObjectInstance, null);
if (value == null) return null;
if (compareTo.Equals(value.ToString(), StringComparison.Ordinal))
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return ValidationResult.Success;
}
public new IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata
metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(FieldName),
ValidationType = "compareemail"
};
rule.ValidationParameters.Add("otherproperty", OtherProperty);
yield return rule;
}
}
JQuery Wire up
jQuery.validator.unobtrusive.adapters.add("comparephone", "[otherproperty]", function
(options) {
options.rules["comparephone"] = options.params.otherproperty;
options.messages["comparephone"] = options.message;
});
jQuery.validator.addMethod("comparephone", function (value, element, params) {
var compareTo = $('[name="' + params + '"]').val();
if (value == compareTo) {
return true;
}
return false;
});
in MVC3 you can add validation to models to check if properties match like so:
public string NewPassword { get; set; }
[Compare("NewPassword",
ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
Is there a way to check that two properties differ like in the following make-believe code?
[CheckPropertiesDiffer("OldPassword",
ErrorMessage = "Old and new passwords cannot be the same")]
public string OldPassword { get; set; }
public string ConfirmPassword { get; set; }
I would do checking in the controller.
In controller:
if(model.ConfirmPassword == model.OldPassword ){
ModelState.AddModelError("ConfirmPassword", "Old and new passwords cannot be the same");
}
In View:
#Html.ValidationMessage("ConfirmPassword")
Hope this helps
Here is what you could use in the model:
public string OldPassword
[NotEqualTo("OldPassword", ErrorMessage = "Old and new passwords cannot be the same.")]
public string NewPassword { get; set; }
And then define the following custom attribute:
public class NotEqualToAttribute : ValidationAttribute
{
private const string defaultErrorMessage = "{0} cannot be the same as {1}.";
private string otherProperty;
public NotEqualToAttribute(string otherProperty) : base(defaultErrorMessage)
{
if (string.IsNullOrEmpty(otherProperty))
{
throw new ArgumentNullException("otherProperty");
}
this.otherProperty = otherProperty;
}
public override string FormatErrorMessage(string name)
{
return string.Format(ErrorMessageString, name, otherProperty);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
PropertyInfo otherPropertyInfo = validationContext.ObjectInstance.GetType().GetProperty(otherProperty);
if (otherPropertyInfo == null)
{
return new ValidationResult(string.Format("Property '{0}' is undefined.", otherProperty));
}
var otherPropertyValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
if (otherPropertyValue != null && !string.IsNullOrEmpty(otherPropertyValue.ToString()))
{
if (value.Equals(otherPropertyValue))
{
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
}
}
return ValidationResult.Success;
}
}
You could also implement class level validation like in the description here: http://weblogs.asp.net/scottgu/archive/2010/12/10/class-level-model-validation-with-ef-code-first-and-asp-net-mvc-3.aspx
Basically you implement the Validate method of IValidatableObject and can access any properties you want.
public class MyClass : IValidateableObject
{
public string NewPassword { get; set; }
public string OldPassword { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext context)
{
if (NewPassword == OldPassword)
yield return new ValidationResult("Passwords should not be the same");
}
}
I don't think that there is already a built-in attribute providing this functionality.
The best approach would be to create your own custom attribute like described in detail there:
http://www.codeproject.com/KB/aspnet/CustomValidation.aspx
Consider I have a these two properties:
public class Test
{
[Required(ErrorMessage = "Please Enetr Age")]
public System.Int32 Age { get; set; }
[Required(ErrorMessage = "Choose an option")]
public System.Boolean IsOld { get; set; }
}
When the user enters for example 15 for Age and choose "Yes" for IsOld, I return an exception that correct Age or IsOld. I've used CustomValidation for it, but because my my validation must be static I can't access to other properties. How can I do this by a DataAnnotation?
You can add data annotations (Custom Validator) to the class itself. In the isValid method of your validation attribute you should then be able to cast the object and test the values that need to be fulfilled.
Example:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// setup test object
Test t = new Test() { Age = 16, IsOld = true };
// store validation results
Collection<ValidationResult> validationResults = new Collection<ValidationResult>();
// run validation
if (Validator.TryValidateObject(t, new ValidationContext(t, null, null), validationResults, true))
{
// validation passed
Console.WriteLine("All items passed validation");
}
else
{
// validation failed
foreach (var item in validationResults)
{
Console.WriteLine(item.ErrorMessage);
}
}
Console.ReadKey(true);
}
}
[TestValidation(ErrorMessage = "Test object is not valid")]
public class Test
{
public int Age { get; set; }
public bool IsOld { get; set; }
}
public class TestValidation : ValidationAttribute
{
public override bool IsValid(object value)
{
bool isValid = false;
Test testVal = value as Test;
if (testVal != null)
{
// conditional logic here
if ((testVal.Age >= 21 && testVal.IsOld) || (testVal.Age < 21 && !testVal.IsOld))
{
// condition passed
isValid = true;
}
}
return isValid;
}
}
}