How to check validation of each object separately? - c#

This my class Obj and Action. But ModelState.isValid checks the whole array obj, but I need a separate object in each cycle to check whether it pass validation.
public class Obj
{
[HiddenInput(DisplayValue = false)]
public string Id { get; set; }
[Required(ErrorMessage = "The field is required")]
public string Name { get; set; }
[Required(ErrorMessage = "The field is required.")]
[Range(1000, 2019, ErrorMessage = "Year of publication must be between 1000 and 2017.")]
[Display(Name = "Year of publication")]
public int Year { get; set; }
[DataType(DataType.MultilineText)]
public string Desc { get; set; }
}
public ActionResult Create(Obj[] obj)
{
foreach (var b in obj)
{
if (ModalState.isValid)
{
//...
}
}
return View();
}

To validate manually with the data annotations you can create your own ValidationContext for the object in question and then run TryValidateObject for each of the objects.
var validationResults = new List<ValidationResult>();
foreach (var b in obj)
{
var context = new ValidationContext(b);
var isValid = Validator.TryValidateObject(b,context,validationResults);
'do whatever.....
}

Try to use these methods: TryValidateModel or ValidateModel

Related

Blazor trigger custom validation message

I have the following class which is being used as an input model for an EditForm in a Blazor server side application.
public class KundeInput
{
[ValidateComplexType]
public List<AnsprechpartnerInput> Ansprechpartner { get; } = new List<AnsprechpartnerInput>();
public string? Kundennummer { get; }
[Required]
[MaxLength(60)]
public string Firma { get; set; } = String.Empty;
[MaxLength(60)]
public string? Name2 { get; set; }
[MaxLength(60)]
public string? Name3 { get; set; }
}
As you can see, my model contains a list of another model called AnsprechpartnerInput. Here is this model:
public class AnsprechpartnerInput
{
public string? Kundennummer { get; set; }
public int Nummer { get; } = -1;
[MaxLength(60)]
[Required]
public string Vorname { get; set; } = String.Empty;
[MaxLength(60)]
[Required]
public string Nachname { get; set; } = String.Empty;
[MaxLength(40)]
[Required]
public string? Bereich { get; set; }
/ * More properties */
}
The validation works fine. However, once I have multiple invalid AnsprechpartnerInput models in my list, the ValidationSummary becomes a mess. Because it displays e.g. 5 times field xyz is invalid.
I know I can set a custom message with the ErrorMessage property but I am not able to use other attributes from my model in this message.
What I want to achive is this:
[Required(ErrorMessage = $"Vorname of {Kundennummer} is required")]
public string Vorname { get; set; } = String.Empty;
I already tried to change the message with reflection but accoridng to Microsoft this way is not recommend or supported
https://github.com/dotnet/aspnetcore/issues/25611
Is there any way to get it to work? I thought of string replacement but I am not sure how I can figure out the right model for my ValidationMessage.
Also is there any way to validate the items of the list by one and get a boolean result? Let's say I want to achive this:
#foreach (var ansprechpartner in Input.Ansprechpartner)
{
if (Input.SelectedAnsprechpartner is null)
Input.SelectedAnsprechpartner = ansprechpartner;
<a #onclick="() => Input.SelectedAnsprechpartner = ansprechpartner"
class="#GetNavListClass(Input.SelectedAnsprechpartner == ansprechpartner)"
id="list-ansprechpartner-tab-#(ansprechpartner.Nummer)"
data-toggle="list"
href="#list-ansprechpartner-#(ansprechpartner.Nummer)"
role="tab"
aria-controls="#(ansprechpartner.Nummer)">
#((MarkupString)(ansprechpartner.Nummer < 0 ? "<span class=\"font-weight-bold\">NEU</span>" : $"({ansprechpartner.Nummer})")) #ansprechpartner.Vorname #ansprechpartner.Nachname
</a>
// When the model ansprechpartner is invalid, I want to display an icon
}
Thanks for any help!
PS: Blazor rocks!
You should use a custom validation attribute where you can explicitly add any error message you want
public class KundennummerValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var model = (AnsprechpartnerInput)validationContext.ObjectInstance;
if(string.IsNullOrEmpty((string)value))
{
return new ValidationResult($"Vorname of {model.Kundennummer} is required", new[] { "Kundennummer" });
}
return ValidationResult.Success;
}
}
then use
[KundennummerValidation]
public string Vorname { get; set; } = String.Empty;
result :
Validation summary:

C# DataAnnotations validation does not work for list

I have following object
public class TestClass
{
[Required]
public int TestId { get; set; }
}
I validate using:
List<ValidationResult> results = new List<ValidationResult>();
var vc = new ValidationContext(data);
if (Validator.TryValidateObject(data, vc, results, true))
return;
This validates perfectly fine if data is of type TestClass but not when I pass list of TestClass items (List<TestClass>)
How can I validate the items withing a list without iterating?
TryValidateObject expects an object and not a list of. You have to write a helper class. Furthermore it even doesn't recursively check the Validation. See this SO question for more...
Usually I add the Validate method in the class that I need to validate, and I foreach the property that has a list of T.
For example:
public class ClassToValidate : IValidatableObject
{
// Property with a non-custom type
[Required(AllowEmptyStrings = false, ErrorMessage = "\"Language\" is required. It can not be empty or whitespace")]
public string Language { get; set; }
// Property with a custom type
public Source Source { get; set; }
// Property with a custom type list
public List<Streaming> StreamingList { get; set; } = new List<Streaming>();
// Validate method that you need to implement because of "IValidatableObject"
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// The validation result that will be returned
List<ValidationResult> result = new List<ValidationResult>();
// Language doesn't need to be added in the Validate method because it isn't a custom object
// Custom single T object to be validated
Validator.TryValidateObject(Source, new ValidationContext(Source), result, true);
// Custom list<T> object to be validated
foreach (var streaming in StreamingList)
{
Validator.TryValidateObject(streaming, new ValidationContext(streaming), result, true);
}
return result;
}
}
public class Source
{
[Range(16, 192, ErrorMessage = "\"Bitrate\" must be between 16 and 192")]
public int? Bitrate { get; set; }
public string Codec { get; set; }
}
public class Streaming
{
[Required(AllowEmptyStrings = false, ErrorMessage = "\"Url\" can not be empty")]
public string Url { get; set; }
[Range(0, 1000, ErrorMessage = "\"Offset\" must be between 0 and 1000")]
public int Offset { get; set; }
}

Validating form when model is a collection of sub models

I've got a view which needs several models to work correctly. So, I created a model which is a collection of multiple (sub) models. This is the model.
public class PolicyDetail
{
public Policy Policy { get; set; }
public IEnumerable<Insured> Insureds { get; set; }
public IEnumerable<Risk> Risks { get; set; }
public IEnumerable<Construction> Constructions { get; set; }
}
And here's an example of what one of the sub models look like, which is an actual entity from the database:
public class Policy
{
[Key]
public int PolicyID { get; set; }
[DisplayName("Policy Number")]
public Guid PolicyNumber { get; set; }
[Required(ErrorMessage = "Please enter a valid Effective Date.")]
[DataType(DataType.DateTime)]
[DisplayName("Effective Date")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
public DateTime EffDate { get; set; }
[Required(ErrorMessage = "Please enter a valid Expiration Date.")]
[DataType(DataType.DateTime)]
[DisplayName("Expiration Date")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
public DateTime ExpDate { get; set; }
public Boolean IsActive { get; set; }
}
This was all working well, right up until I tried to submit a form with errors in it to test the validation. I should have seen this coming (maybe?) but because the actual model doesn't have any validation tags on it, it always passes the if (ModelState.IsValid) check. Is there some way to enforce, or inherit, all of the Data Annotations from the sub classes?
Or, am I going about this all wrong, using a model which is a collection of other models? The thing is, I want to be able to edit/add multiple db entities from the same view.
EDIT:
This article by Josh Carroll looks to be EXACTLY what I need. But when I implement it, I get a Null Object error. Here's what I'm doing:
public class PolicyDetail
{
[Required, ValidateObject]
public Policy Policy { get; set; }
public IEnumerable<Insured> Insureds { get; set; }
public IEnumerable<Risk> Risks { get; set; }
public IEnumerable<Construction> Constructions { get; set; }
}
Then in the override method he provides:
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var results = new List<ValidationResult>();
var context = new ValidationContext(value, null, null);
Validator.TryValidateObject(value, context, results, true);
if (results.Count != 0)
{
var compositeResults = new CompositeValidationResult(String.Format("Validation for {0} failed!", validationContext.DisplayName));
results.ForEach(compositeResults.AddResult);
return compositeResults;
}
return ValidationResult.Success;
}
}
the parameter "value" comes in null, so it errors on this line:
Validator.TryValidateObject(value, context, results, true);
Am I missing something? Doing something wrong?
You can manually call the validations on the sub-models using this: https://msdn.microsoft.com/en-us/library/dd411772.aspx
var context = new ValidationContext(model.Policy, serviceProvider: null, items: null);
var validationResults = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(model.Policy, context, validationResults, true);
You can then use the ModelState.AddModelError to build the response from that.
Definitely not the most elegant possible solution, but might be easier than rewriting what you have.

MVC 4 Conditional Model Validation with Entity Framework

Is it possible to place conditions on a ViewModel where data equals a specific value in one field within the entity framework, the required element or entire object is removed from the ViewModel before using TryValidateModel on the ViewModel?
I would like to remove HomeValue and PurchasePrice from the Model validation where OwnOrRent (model.OwnOrRent) is not equal to 1 or 9.
Model.cs
public class Address
{
public int Id { get; set; }
[DisplayName("House Name or Number")]
[StringLength(50)]
public string HouseNameOrNumber { get; set; }
[DisplayName("Post Town")]
[StringLength(50)]
public string PostTown { get; set; }
[Required(ErrorMessage = "Own or Rent is Required")]
[DisplayName("Own or Rent")]
[StringLength(50)]
public string OwnOrRent { get; set; }
[Required(ErrorMessage = "Mortgage/Rent Amount is Required")]
[DisplayName("Mortgage/Rent Amount")]
[StringLength(50)]
public string MortgageRent { get; set; }
[Required(ErrorMessage = "Home Value is Required")]
[DisplayName("Home Value")]
[StringLength(50)]
public string HomeValue { get; set; }
[Required(ErrorMessage = "Purchase Price is Required")]
[DisplayName("Purchase Price")]
[StringLength(50)]
public string PurchasePrice { get; set; }
}
HomeController.cs
public ActionResult Application(int id)
{
var viewAddressModel = new Address();
using (
var dbEntities = new DbEntities(new EntityConnection(_df.DbEntities)))
{
var model = dbEntities.Applications.Single(e => e.Id == id);
viewAddressModel.Id = Id;
viewAddressModel.HouseNameOrNumber = model.HouseNameOrNumber;
viewAddressModel.PostTown = model.PostTown;
viewAddressModel.OwnOrRent = GetStatus(model.OwnOrRent);
viewAddressModel.MortgageRent = model.MortgageRent.ToString();
viewAddressModel.HomeValue = model.HomeValue;
viewAddressModel.PurchasePrice = model.PurchasePrice;
if (model.OwnOrRent != "1" || model.OwnOrRent != "9")
{
ModelState.Remove("HomeValue");
ModelState.Remove("PurchasePrice");
}
if (!TryValidateModel(viewAddressModel))
{
return PartialView("Address", viewAddressModel);
}
}
var vm = new ApplicationViewModel { Item = CreateApp(id) };
return PartialView("Application", vm);
}
As you can see I have tried to use ModelState.Remove but this has no effect.
Any assistance with this would be much appreciated?
Based on your comments you want to populate a model from the database, then validate it (because its old data which may not be valid), but not display errors for HomeValue or PurchasePrice based on the value of OwnOrRent, in which case you need to call TryValidateModel first, then remove ModelState errors
var viewAddressModel = new Address();
.... // set values
if (!TryValidateModel(viewAddressModel))
{
if (model.OwnOrRent != "1" || model.OwnOrRent != "9")
{
if (ModelState.ContainsKey("HomeValue"))
{
ModelState["HomeValue"].Errors.Clear();
}
if (ModelState.ContainsKey("PurchasePrice"))
{
ModelState["PurchasePrice"].Errors.Clear();
}
}
}
You can now use if (ModelState.IsValid) to check if there are any other validation errors and return the appropriate view
Side note: I just used your if condition relating to the OwnOrRent value, but I suspect what you really want is
if (!(model.OwnOrRent == "1" || model.OwnOrRent == "9"))
There is a thread about the different options to do conditional validation:
ASP.NET MVC Conditional validation

Generics where <t> Class library and MVC app

I'm trying to learn how to use the 'where' in Generic, I seem to have it working but I not sure if how I have done it is the correct way.
Currently I have in a class library:
public class NewMembersViewModel
{
public NewMembersViewModel()
{
Id = Guid.NewGuid().ToString();
}
[Display(Name = "Username")]
[RemoteUsernameValidation("IsUserNameTaken", "Register", ErrorMessage = "Username already taken")]
public string MemberUsername { get; set; }
[Display(Name = "Password")]
public string MemberPassword { get; set; }
[Display(Name = "Email")]
[RemoteEmailValidation("IsEmailTaken", "Register", ErrorMessage = "Email already in use")]
public string MemberEmail { get; set; }
[Display(Name = "Mobile Number")]
[RemoteMobileValidation("IsMobileTaken", "Register", ErrorMessage = "Mobile Number already in use")]
public string MemberMobile { get; set; }
[Display(Name = "Forename")]
public string MemberForename { get; set; }
[Display(Name = "Surname")]
public string MemberSurname { get; set; }
public string Id { get; set; }
public int VerificationCode { get; set; }
}
And
public static class SqlDatabaseMethods<T> where T : NewMembersViewModel
{
public static bool LoginUser(T member)
{
var p = member.MemberEmail;
return true;
}
public static bool AddNewMember(T member)
{
var name = member.MemberForename;
return true;
}
}
Then in my MVC app I have:
bool name = SqlDatabaseMethods<NewMembersViewModel>.AddNewMember(model);
if (name)
{
var test = "works";
}
This does work, can anyone tell me whether I doing it correctly, as all examples I have seen appear to be console apps
Generics make sense when you need have some functionality which is common across multiple classes. In this case where clause defines what the constraints of your generic type are. In such case generic methods help to re-use functionality.
In your case it doesn't seem that SqlDatabaseMethods<T> could be re-used more than once, as model NewMembersViewModel seems to be very specific and hardly re-usable elsewhere.
In your code you are using generics correctly, but in my opinion you don't need generics here at all. You could just as easily use the following code without the overhead of generics:
public static class SqlDatabaseMethods
{
public static bool LoginUser(NewMembersViewModel member)
{
var p = member.MemberEmail;
return true;
}
public static bool AddNewMember(NewMembersViewModel member)
{
var name = member.MemberForename;
return true;
}
}
And then call the method like this:
bool name = SqlDatabaseMethods.AddNewMember(model);
if (name)
{
var test = "works";
}

Categories

Resources