Multi country validation rules - c#

In my multi country site, I have a form for address creation. I want customize my validation rules by country (For example : FR ZipCode length 8, USA : 10...), and I hesitate between many solution : one ViewModel by Country, Parameterized annotation, DataFilter... Any idea ?
public class Address
{
[Required]
public string Name { get; set; }
[StringLength(lengthByCountry)]
public string ZipCode { get; set; }
}

You can use the CustomValidationAttribute to write your own validator that does country-specific validation of ZIP codes depending on the country.

If the country and zip code need to be specified within the same submission, then I'd suggest making your model implement IValidatableObject, which allows you to validate based on combinations of values.
public class Address : IValidatableObject
{
[Required]
public string Name { get; set; }
public string Country { get; set; }
public string ZipCode { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
switch (Country)
{
case "France":
if (ZipCode.Length < 8)
results.Add(
new ValidationResult("French zip codes must be at least 8 characters", new List<string> { "ZipCode" })
);
break;
case "U.S.":
if (ZipCode.Length < 10)
results.Add(
new ValidationResult("American zip codes must be at least 10 characters", new List<string> { "ZipCode" })
);
break;
// Etc.
}
return results;
}
}

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# Copy List items to Object Arrays

I have a list created from a stored procedure using EF6.0
I have also created 3 classes
public class Resas
{
public string todo{ get; set; }
public string prop { get; set; }
public string Code { get; set; }
public string statusCode { get; set; }
public string checkin { get; set; }
public string checkout { get; set; }
public List<profiles> profiles { get; set; }
}
public class profiles
{
public string action { get; set; }
public string id { get; set; }
public string profileType { get; set; }
public string title { get; set; }
public string firstName { get; set; }
public string middleName { get; set; }
public string lastName { get; set; }
public List<emailAddresses> emailAdresses { get; set; }
}
public class emailAddresses
{
public string emailAddress { get; set; }
public string emailAddress2 { get; set; }
}
I am doing a for-loop in the list and I need to get certain columns and put it in the array (I will put two, to keep it simple)
myEntities db = new myEntities();
List<rev_Result> revList = new List<rev_Result>();
revList.Clear();
revList = db.rev().ToList();
for (int i = 0; i < revList.Count(); i++)
{
Resas resas = new Resas();
profiles[] profiles = new profiles[1];
resas.todo = revList[i].todo;
resas.profiles[0].lastName = revList[i].lastName;
}
I am not familiar with C# as you can see from the psedo-code above.
I cannot figure out how to feed the Resas with data and then its Profile with data and then move to the next Resas entry.
Any help appreciated.
That's fairly simple using Linq:
Resas resas = new Resas();
resas.profiles = revList
.Select(x => new profiles() { action = x.todo, lastName = x.lastName })
.ToList();
What's happening here is: You loop through every entry in revList and get your wanted data structure (that's what Select is doing). x refers to the current entry in the loop, while the stuff to the right side of the arrow is you 'output': a new instance of your profiles class with the members assigned accordingly. The result of all of this is then converted to a list (before ToList(), think of it as a recipe to create the list) and assigned to resas.profiles.
By the way, a word on conventions: Usually, in C#, you would give your classes a name that starts with a capital letter. Also, your profiles class seems to contain data of exactly one profile, so a better name might be Profile. This also makes your data structure more clear, since List<profiles> seems to be a list of lists of profiles - but that's not what it actually is, is it?
Furthermore, Members generally start with a capital letter as well, so instead of action, lastName, you'd have: Action and LastName.
You can try with Linq. This is the code that should solve your issue, but Resas class doesn't have action property:
List<Resas> ls = revList.Select(x => new Resas() {
action = x.todo,
profiles = new List<profiles>() {
new profiles { lastName = x.lastName }
}
).ToList();
If you need to use action property of inprofiles` class:
List<Resas> ls = revList.Select(x => new Resas() {
profiles = new List<profiles>() {
new profiles {
action = x.todo,
lastName = x.lastName
}
}
).ToList();

Validate model on specific string values

I'm working on a Web API 2 project. besides the requirement that some properties are required, some only can have specific values.
One option is that I could try to save the model to the database (EF6) and create some logic while saving, but I think it is better to validate if the correct value is set before I make a call to the database. Does data annotations provide an attribute like Range but then for specific string values like in the example below? Or do I have to write my own validator attribute?
public class Person {
public int PersonID { get; set; }
[Required]
public string FirstName { get; set; }
public string LastName { get; set; }
[StringRange("M","F")]
public string Gender { get; set; }
}
In the above example, when a post is done to the controller, the only values to accept are "M" or "F".
In case anyone stumbles upon this thread in the future, I took it a little further and added a public string array property accepting the allowable values in the validation filter. This will allow you to provide a collection of valid strings in the attribute decorator.
This way, you now have a generic reusable attribute you can use anytime to limit a model string property to a predefined set of values. Since it's a validation attribute, you can decorate it with a more informative error message as well.
Example Usage:
public class Person {
[StringRange(AllowableValues = new[] { "M", "F" }, ErrorMessage = "Gender must be either 'M' or 'F'.")]
public string Gender { get;set; }
}
String Attribute:
public class StringRangeAttribute : ValidationAttribute
{
public string[] AllowableValues { get; set; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (AllowableValues?.Contains(value?.ToString()) == true)
{
return ValidationResult.Success;
}
var msg = $"Please enter one of the allowable values: {string.Join(", ", (AllowableValues ?? new string[] { "No allowable values found" }))}.";
return new ValidationResult(msg);
}
}
To validate the Gender property I've created a custom validation attribute by creating a new class (attribute):
using System;
using System.Collections.Generic;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace MyProject.Models.Validation
{
public class StringRangeAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if(value.ToString() == "M" || value.ToString() == "F")
{
return ValidationResult.Success;
}
return new ValidationResult("Please enter a correct value");
}
}
}
you can use reqular expression like this:
[RegularExpression("M|F", ErrorMessage = "The Gender must be either 'M' or 'F' only.")]
public string Gender { get; set; }
but in my api it will show error message when i passed data
so you can add
[StringLength(1, MinimumLength = 1, ErrorMessage = "The Gender must be 1 characters.")]
final code:
[StringLength(1, MinimumLength = 1, ErrorMessage = "The Gender must be 1 characters.")]
[RegularExpression("M|F", ErrorMessage = "The Gender must be either 'M' or 'F' only.")]
public string Gender { get; set; }
[Required]
[RegularExpression("value1|value2|value3|...", ErrorMessage = "YourMessageHere")]
public string Gender { get; set; }

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

DataAnnotation to compare two properties

Is there any way of using data annotations to compare two form field (eg. to confirm an email address) are the same, before allowing the form to be posted?
eg. can the regular expression data annotation use the match function to reference another property in a ViewModel?
Use the CompareAttribute
public string EmailAddress {get; set;}
[Compare(nameof(EmailAddress), ErrorMessage = "Emails mismatch")]
public string VerifiedEmailAddress { get; set; }
As one possibe option self-validation:
Implement an interface IValidatableObject with method Validate, where you can put your validation code.
public class TestModel : IValidatableObject
{
public string Email{ get; set; }
public string ConfirmEmail { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Email != ConfirmEmail)
{
yield return new ValidationResult("Emails mismatch", new [] { "ConfirmEmail" });
}
}
}
Please notice: this is only server-side validation.

Categories

Resources