Fluent validation rule uppers without corresponding rule - asp.net-mvc-3

I have strange issue. ModelState has error. But I don`t have a rule for it. No filters, no rules in validator file.
My code. ViewModel:
[Validator(typeof(TestValidation))]
public class PayerPayRateViewModel
{
public int TestId { get; set; }
public bool AllServices { get; set; }
public int ParentEntityId { get; set; }
}
Validator
public class TestValidation : BaseEntityRepositoryValidator<Core.Domain.Model.Entities.Payer, PayerPayRateViewModel>
{
public TestValidation()
{
RuleFor(x => x.ParentEntityId).Must(CheckUniqueService);
}
protected bool CheckUniqueService(PayerPayRateViewModel model, int value)
{
if (model.AllServices)
{
return true;
}
return false;
}
}
And if I have TestId with value 0 I get "TestId: Field is required".
When I remove validation attribute from Viewmodel class I get "A value is required." error.
Why it happens?

Because you are attempting to bind an empty string to a non-nullable type. If you want this to happen use nullable types:
[Validator(typeof(TestValidation))]
public class PayerPayRateViewModel
{
public int? TestId { get; set; }
public bool AllServices { get; set; }
public int ParentEntityId { get; set; }
}
By default there's an implicit Required attribute applied to all non-nullable types (think integers, datetimes, decimals, ...).
By the way you could disable this default behavior:
DataAnnotationsModelValidatorProvider
.AddImplicitRequiredAttributeForValueTypes = false;

Related

FluentValidation Set Valid Result to a Custom Property

I am validating the content for file import and I have an IsValid property for each line.
public class Header
{
public int LineNumber { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public bool IsValid { get; set; }
}
public class Detail
{
public int LineNumber { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
public bool IsValid { get; set; }
}
public class Trailer
{
public int LineNumber { get; set; }
public string Property1 { get; set; }
public bool IsValid { get; set; }
}
public class ImportFile
{
public Header Header { get; set; }
public List<Detail> Details { get; set; }
public Trailer Trailer { get; set; }
}
and my validators look somewhat like:
public class DetailValidator : AbstractValidator<Detail>
{
public DetailValidator()
{
RuleFor(d => d.Property1)
.Cascade(CascadeMode.Stop)
.NotEmpty()
.WithState(d => d.LineNumber)
.Length(3)
.WithState(d => d.LineNumber);
RuleFor(d => d.Property2)
.Cascade(CascadeMode.Stop)
.NotEmpty()
.WithState(d => d.LineNumber)
.MaximumLength(50)
.WithState(d => d.LineNumber);
...
}
}
public class ImportFileValidator : AbstractValidator<ImportFile>
{
public ImportFileValidator()
{
RuleFor(f => f.Header)
.SetValidator(new HeaderValidator());
RuleForEach(f => f.Details)
.SetValidator(new DetailsValidator());
...
}
}
After I call the validation, I wanted to set the IsValid property of each line of the file (be it header, detail or trailer) base from the result of the validation.
What is possible for now is, since I am using WithState to store the LineNumber, I can match the ValidationResult against the ImportFile instance to set each line's validity like below:
ImportFile file = // parsed file content
var result = new ImportFileValidator().Validate(file);
foreach (var detail in file.Details)
{
var error = result.Errors.FirstOrDefault(e =>
Convert.ToInt32(e.CustomState) == detail.LineNumber);
detail.IsValid = error == null;
}
And I have to check for the header and trailer as well.
Is there a way I can do this inside the validators? I am trying to explore the FluentValidation's documentation, but I can't seem to find what I needed there.
As I was exploring the available methods in FluentValidation, I saw OnFailure and OnAnyFailure methods. This methods might be a good help to what I needed to do, but the problem is they're obsolete as of 10.3.0 and will be removed on version 11. They're suggesting to use a custom validator instead.
The Header, Detail and Trailer Abstract Validators remain as is.
I created custom validator extensions for those 3.
Each extension methods creates an instance of the corresponding validator and executes it. I can make them generic for header, detail and trailer since they will do the same thing, set IsValid property to the validation result.
public static IRuleBuilderOptionsConditions<ImportFile, T> IsHeaderValid<T>(this IRuleBuilder<ImportFile, T> ruleBuilder)
where T : Header
{
return builder.Custom((header, context) =>
{
// Create the Header Abstract Validator Instance
var validator = new HeaderValidator();
var result = validator.Validate(Header);
header.IsValid = result.IsValid;
// Pass the errors to the context
result.Errors.ForEach(context.AddFailure);
}
}
I had to change the ImportFileValidator to call the custom validators, instead of using setvalidator.
The ImportFileValidator looks like this:
public class ImportFileValidator : AbstractValidator<ImportFile>
{
public ImportFileValidator()
{
RuleFor(f => f.Header)
.IsHeaderValid();
RuleForEach(f => f.Details)
.IsDetailValid();
...
}
}
This is pretty much how I was able to set the IsValid property without having to do the matching I initially did in the question.

ModelState.Isvalid not work for default value of boolean field

WEB API --->
public async Task<IHttpActionResult> CreatePost(ChildClient c)
{
if(!ModelState.IsValid) {
throw ...
}
..
}
public class Client
{
[Required]
public bool HasBaseValue { get; set; } = true;
[Required]
public string Name { get; set; } = "stringvalue";
}
public class ChildClient : Client
{
[Required]
public bool HasFieldValue { get; set; } = true;
[Required]
public string Name1 { get; set; } = "stringvalue";
}
ModelState.Keys gives following errors: HasBaseValue,HasFieldValue if both fields are not supplied.
why it still shows in error field even though default value is set.
NOTE: default values are already populated in 'c object' when I debug and check by breakpoint.
Achieve by skipping those fields from ModelState.IsValid to validate. As those default values fields are not posted so, ModelState.IsValid considering as invalid.(because, default value populate from API but ModelState.IsValid only validate posted value).

How to pass object in method to call service API in ASP.NET Boilerplate?

I have defined a method defined in AppService file, Signature of method is public PagedResultDto<FetchData> GetSearchData(FetchDataInput searchInput). I'm calling this method from Angular code, but service-proxoes.ts file has generated method in which I need to pass all the parameters one by one.
public class FetchDataInput: PagedAndSortedInputDto, IShouldNormalize
{
public int DataLevel { get; set; }
public int DataType { get; set; }
public string DataCode { get; set; }
public string DescLong { get; set; }
public string LanguageCode { get; set; }
public string DataParent { get; set; }
public void Normalize()
{
if (string.IsNullOrEmpty(Sorting))
{
Sorting = "DataCode";
}
}
}
Service-Proxies.ts file:
getSearchData(dataLevel: number, dataType: number, dataCode: string, descLong: string, languageCode: string, dataParent: string, sorting: string, maxResultCount: number, skipCount: number): Observable<PagedResultDtoOfFetchData> {
So I have to call the getSearchData method by the following way.
this._dataService.getSearchData(AppConsts.dataLevelType, undefined, undefined,
undefined, this.currentLanguage.name, undefined, undefined, undefined, undefined).subscribe((result) => {
//result.items;
});
So I have to pass all the parameters, but if let's say there are 100 parameters, it will be error prone and not good programming style. So it has to take a class object that's it. So is there any way to do the this?
You can create a object to store those parametter like this:
public class FetchDataInput: PagedAndSortedInputDto, IShouldNormalize
{
public SearchModel searchModel{get; set;}
public void Normalize()
{
if (string.IsNullOrEmpty(Sorting))
{
Sorting = "DataCode";
}
}
}
public class SearchModel
{
public int DataLevel { get; set; }
public int DataType { get; set; }
public string DataCode { get; set; }
public string DescLong { get; set; }
public string LanguageCode { get; set; }
public string DataParent { get; set; }
}
...And in the getSearchData, tried to serialize the model and pass it to your api:
_$SearchModelForm= _modalManager.getModal().find('form[name=SearchForm]');
var searchModel = _$SearchModelForm.serializeFormToObject();
this._dataService.getSearchData(searchModel);

Cannot apply indexing with [] to an expression of type ICollection

Can someone please provide code to fix this error?
"Cannot apply indexing with [] to an expression of type 'ICollection'
Essentially, I'm trying to save/bind a value from a collection of objects.
#model MVC3.Models.Parent
#Html.EditorFor(model => model.Bs[0].Val)
public class A
{
public int Name { get; set; }
public virtual ICollection<B> Bs { get; set; }
}
public class B
{
public int Val { get; set; }
public virtual A A { get; set; }
}
ICollections are not ordered, so that cannot be indexed.
Instead, you should use a separate ViewModel class with IList<T> property.
Use IList
public class A
{
public int Name { get; set; }
public virtual IList<B> Bs { get; set; }
}

asp.net mvc2 validations

I am using DataAnnotations for validation (including client side)
I have a form with multiple fields. Basic validation for individual fields work fine. Now there are a couple of fields of which atleast one needs to have a value (if there are 3 fields then either 1st or 2nd or 3rd field should have a value).
I have read quite a few posts on this site and couple of blog entries. But I couldn't find a solution that works in the above mentioned scenario. I might have missed something or doing it incorrectly.
Can you help with this please?
try this
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class EitherOr : ValidationAttribute
{
private const string _defaultErrorMessage = "'{0}' OR '{1}' OR '{2}' must have a value";
private readonly object _typeId = new object();
public EitherOr(string prop1, string prop2, string prop3)
: base(_defaultErrorMessage)
{
Prop1 = prop1;
Prop2 = prop2;
Prop3 = prop3;
}
public string Prop1 { get; private set; }
public string Prop2 { get; private set; }
public string Prop3 { get; private set; }
public override object TypeId
{
get
{
return _typeId;
}
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, Prop1, Prop2,Prop3);
}
public override bool IsValid(object value)
{
if(string.IsNullOrEmpty(Prop1)&&string.IsNullOrEmpty(Prop2) && string.IsNullOrEmpty(Prop3))
{
return false;
}
return true;
}
then mark your class with the EitherOr attribute:
[EitherOr("Bar","Stool","Hood", ErrorMessage = "please supply one of the properties")]
public class Foo
{
public string Bar{ get; set;}
public string Stool{ get; set;}
public string Hood{ get; set;}
}
Please note that i made use of string properties, if your property is of other type, makle sure to change the IsValid(object value) validation

Resources