use IModelValidator to Create a Custom Property Validation Attribute - c#

Below is a custom property validation from my textbook
public class MustBeTrueAttribute : Attribute, IModelValidator
{
public bool IsRequired => true;
public string ErrorMessage { get; set; }
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{
bool? value = context.Model as bool?;
if (!value.HasValue || value.Value == false)
{
return new List<ModelValidationResult> {
new ModelValidationResult("", ErrorMessage) // why first argument has to be empty?
};
}
else
{
return Enumerable.Empty<ModelValidationResult>();
}
}
}
public class Appointment
{
public DateTime Date { get; set; }
[MustBeTrue(ErrorMessage = "You must accept the terms")]
public bool TermsAccepted { get; set; }
}
I have two questions:
Q1- IModelValidator interface doesn't define the IsRequired property, where it comes from and how this property is going to be used?
Q2- why the first argument(memberName) in the ModelValidationResult's constructor has to be empty "", under what circumstances we need to specify a value

Q1: IModelValidator interface doesn't define the IsRequired property, where it comes from and how this property is going to be used?
Ans: In this case, the property IsRequired is useless, because it isn't used in your code.
Q2: why the first argument(memberName) in the ModelValidationResult's constructor has to be empty "", under what circumstances we need to specify a value
Ans: Actually, it doesn’t have to be empty. It depends on what level you'd like to validate. Once you registered your custom validator, you might call it in your Controller to validate your model. So if this validator is only for one model, you can validate all the properties with a Switch...Case statement and give those properties a specific name. On the other hand, if this validator will be used to validate many different models. You may need to consider whether it is suitable to specify a value.
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{
if (context != null)
{
switch (context.ModelMetadata.PropertyName)
{
case "TermsAccepted":
if (!context.Model.TermsAccepted) {
return new ModelValidationResult[]
{
new ModelValidationResult
{
MemberName = "TermsAccepted",
Message = "You must accept the terms"
}
};
}
break;
default:
}
return new List<ModelValidationResult>
{
new ModelValidationResult("", ErrorMessage)
};
}
return Enumerable.Empty<ModelValidationResult>();
}

Related

How to pass a method or parameter to an action filter in ASP.Net MVC

I'm going to handle authentication and authorization in an action filter and create an action filter like below:
public class Auth : ActionFilterAttribute
{
public int Access { get; set; }
public string Roles { get; set; } = "Default";
public Func<bool> AuthFunc { get; set; }
public override void OnActionExecuting(HttpActionContext actionContext)
{
string UserId = HttpContext.Current.User.Identity.GetUserId();
//Authentication
if (Roles != "Default" && UserManager.IsInRole(UserId, Roles))
{
//Authorization
if (AuthFunc) { base.OnActionExecuting(actionContext); }
else
{
var response = actionContext.Request.CreateResponse(HttpStatusCode.Redirect);
Uri requestUrl = actionContext.Request.RequestUri;
response.Headers.Location = new Uri($"{requestUrl.Scheme}://{requestUrl.Host}:{requestUrl.Port}");
actionContext.Response = response;
}
}
else
{
var response = actionContext.Request.CreateResponse(HttpStatusCode.Redirect);
Uri requestUrl = actionContext.Request.RequestUri;
response.Headers.Location = new Uri($"{requestUrl.Scheme}://{requestUrl.Host}:{requestUrl.Port}");
actionContext.Response = response;
}
}
}
And in the controller:
[Auth(Roles="Teacher" , Access = (short)TableEnum.Course , AuthFunc = Courses.CheckCoursesOfTeacher(CourseId))]
public ActionResult ShowExerciseAnswers(int CourseId,int ExerciseId)
{
return View(model: ChapterExerciseAnswer.ExerciseAnswerList(CourseId,ExerciseId));
}
The AuthFunc method maybe has multiple inputs but just a bool return value.
How to pass AuthFunc (the Courses.CheckCoursesOfTeacher(CourseId) method) to action filter?
How to get CourseId action parameter in action filter attribute (pass CourseId or ExerciseId as an attribute value)?
What is the best way of handling these issues(functions and variables can't be sent to an action filter)?
The Problem with Passing Functions in Attribute Parameters
I found myself looking for a solution like this recently. Parameters for attributes have to follow the following rules, per MS Docs:
Parameters to an attribute constructor are limited to simple types/literals: bool, int, double, string, Type, enums, etc and arrays of those types. You can not use an expression or a variable. You are free to use positional or named parameters.
Because of this, passing a function to the filter via an attribute parameter is not something we can do. There are probably lots of alternatives, but here's what I chose to do:
A Solution
I used dependency injection to inject my action filter with a manager, and then used Reflection to tell the filter which method on the manager to execute. Here's what it looks like:
Class:
public class Phone : AuditBase
{
...other props...
[AuditRuleset(AuditRule.PhoneNumber)]
public string Number { get; set; }
}
Enum:
public enum AuditRule
{
PhoneNumber // You can add [Description] if you want
}
Attribute:
public class AuditRulesetAttribute : Attribute
{
private readonly AuditRule _rule;
public AuditRulesetAttribute(AuditRule rule) => _rule = rule;
}
Filter:
public class FormAuditActionFilter : IActionFilter
{
private ILog _log { get; set; }
private IFormAuditor _auditor { get; set; }
public FormAuditActionFilter(ILog log, IFormAuditor auditor)
{
_log = log;
_auditor = auditor;
}
...lots of filter code...
... The following is from OnActionExecuted, having stored the props of the submitted object in objectProperties...
foreach(PropertyInfo propertyInfo in objectProperties)
{
// Check first for any special audit comparison rules which should be followed
var auditRuleAttributes = propertyInfo.CustomAttributes
.Where(x => x.AttributeType.Name == typeof(AuditRulesetAttribute).Name)
.ToList();
if (auditRuleAttributes.Any())
{
IEnumerable<IList<CustomAttributeTypedArgument>> attrList = auditRuleAttributes
.Select(x => x.ConstructorArguments);
foreach(IList<CustomAttributeTypedArgument> attr in attrList)
foreach(CustomAttributeTypedArgument arg in attr)
if (_auditRuleManager.IsChanged(oldValue, newValue, (AuditRule)arg.Value))
result.Add(BuildValueHistory(propertyInfo.Name, oldValue, newValue));
continue;
}
}
...lots more filter code...
}
AuditRuleManager:
public class AuditRuleManager : IAuditRuleManager
{
public bool IsChanged(object val1, object val2, AuditRule rule)
{
object[] objArray = {val1, val2};
var comparisonResult = typeof(AuditRuleManager)
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
.Single(m => m.Name == rule.GetDescription()) // Try to get description, but falls back to name by default
.Invoke(this, objArray) as bool?;
return (bool)comparisonResult; // Throw an exception if the comparison result was not a valid bool
}
// Compare phone numbers with special rules, and return their equality
private bool PhoneNumber(object val1, object val2) // NOTE: Name of method matches name of enum value
=> GetNumbersFromString(val1 as string) != GetNumbersFromString(val2 as string);
The last piece that took me a while was the DI for the filter using Ninject. Here's how it worked in my
Global.asax.cs:
kernel.BindFilter<FormAuditActionFilter>(FilterScope.Action, 0)
.WhenActionMethodHas<FormAuditAttribute>()
.WithConstructorArgument("log", log)
.WithConstructorArgument("auditor", auditManager);
Summary
Instead of passing a function as an attribute parameter, I used DI to inject a manager into my filter. This gives your filter access to the functions you want. Second, I used an enum to hold the name of the function that should be executed. So essentially, all you have to do to create a new function and execute it with a parameter is to:
Add it to the enum
Add a function of the same name to the manager
I hope this helps!
Further Reading
https://blogs.cuttingedge.it/steven/posts/2014/dependency-injection-in-attributes-dont-do-it/

C# custom attribute validation in console environment

My question is about validation using custom attributes in C#.
I don't quite understand how the validation works. I have declared an attribute with the validation rule in it but when the error should be thrown it is not.
Attribute:
[AttributeUsage(AttributeTargets.Property)]
public class NotNullAttribute : Attribute
{
public bool IsValid(object value)
{
if (value is string && (string)value != "")
{
return false;
}
return true;
}
}
Inside the attribute I check if the property is of type string and if its value is an empty string because that is what I have to check.
The task is to check if a property is a string and if its an empty string then its not valid, otherwise it is.
My Person class:
class Person
{
[NotNull]
public string Name { get; set; }
}
Here I am applying the custom attribute.
Main method:
class Program
{
static void Main(string[] args)
{
Person p1 = new Person();
p1.Name = "";
Console.WriteLine("Validation done");
Console.ReadKey();
}
}
This is where I instantiate the Person class and assign an empty string to the Name property. This is where the error should be thrown I guess.
So my question is why isn't the validation applied? Should I have called the IsValid method from the attribute it self somehow?
I would take some explanation about this, thank you in advance!
The attribute itself is just a "decorator" of the property. If nothing calls it, it will not be automatically executed nor used.
In your case, however, I don't see the point of using an attribute, when you can use property itself:
private string _name = "";
public string Name
{
get
{
return _name;
}
set
{
if ( string.IsNullOrEmpty(value) )
{
//throw or fallback
}
else
{
_name = value;
}
}
}
Doing basic value validation is exactly the job property setters are great for. In case someone uses an invalid value, you can throw an exception, or set a fallback value for example.
If you would still prefer using attributes, you still need to have some code that performs the validation itself. And still, anyone can assign any valid value to the property, unless validation is performed.
For example ASP.NET MVC uses attribute validation during Model Binding - it checks the validation attributes on the bound model class and verifies it before the action method begins executing.
Example of attribute validation
Here is a simple example of how to make your code work with reflection.
First here is a slightly updated version of the validation attribute:
[AttributeUsage(AttributeTargets.Property)]
public class NotNullAttribute : Attribute
{
public bool IsValid(object value)
{
if (!string.IsNullOrEmpty(value as string))
{
return false;
}
return true;
}
}
Your code actually only allowed a null or "" value, which I guess is opposite of what you wanted. This version is valid only when the string is not null and not empty.
Now create a Validate method in your Program class:
private static bool Validate(object model)
{
foreach (var propertyInfo in model.GetType().GetProperties())
{
foreach (var attribute in propertyInfo.GetCustomAttributes(true))
{
var notNullAttribute = attribute as NotNullAttribute;
if (notNullAttribute != null)
{
if (!notNullAttribute.IsValid(propertyInfo.GetValue(model)))
{
return false;
}
}
}
}
return true;
}
This basically gathers all properties of the type of the passed in parameter, checks all attributes of the properties for NotNullAttribute and then executes the attribute's IsValid method against the current value from the model.
Finally here is how you can call it from Main:
static void Main(string[] args)
{
Person p1 = new Person();
p1.Name = "d";
if (Validate(p1))
{
Console.WriteLine("Valid");
}
else
{
Console.WriteLine("Invalid");
}
Console.WriteLine("Validation done");
Console.ReadKey();
}
Now, if you are planning on adding more validation attributes, I would create an interface first:
public interface IValidationAttribute
{
bool IsValid(object value);
}
Then derive all your validation attributes from IValidationAttribute and in Validate method use IValidationAttribute in place of NotNullAttribute. This way the code becomes more future-proof as you can just program against the interface and add new validation attributes anytime.
public class BankAccount
{
public enum AccountType
{
Saving,
Current
}
[Required(ErrorMessage="First Name Required")]
[MaxLength(15,ErrorMessage="First Name should not more than 1`5 character")]
[MinLength(3,ErrorMessage="First Name should be more than 3 character")]
public string AccountHolderFirstName { get; set; }
[Required(ErrorMessage="Last Name Required")]
[MaxLength(15,ErrorMessage="Last Name should not more than 1`5 character")]
[MinLength(3,ErrorMessage="Last Name should be more than 3 character")]
public string AccountHolderLastName { get; set; }
[Required]
[RegularExpression("^[0-9]+$", ErrorMessage = "Only Number allowed in AccountNumber")]
public string AccountNumber { get; set; }
public AccountType AcType { get; set; }
[AccountBalaceCheckAttribute]
public double AccountBalance { get; set; }
}
How to Validate
public class GenericValidator
{
public static bool TryValidate(object obj, out ICollection<ValidationResult> results)
{
var context = new ValidationContext(obj, serviceProvider: null, items: null);
results = new List<ValidationResult>();
return Validator.TryValidateObject(
obj, context, results,
validateAllProperties: true
);
}
}
Example
static void Main(string[] args)
{
var bankAccount = new BankAccount();
ICollection<ValidationResult> lstvalidationResult;
bool valid = GenericValidator.TryValidate(bankAccount, out lstvalidationResult);
if (!valid)
{
foreach (ValidationResult res in lstvalidationResult)
{
Console.WriteLine(res.MemberNames +":"+ res.ErrorMessage);
}
}
Console.ReadLine();
}

Save value in uppercase when adding row to database

I have three columns in the db table that looks as follow:
When I add a new row, it should store the value on column fieldname in uppercase. How can I do that?
Since you tagged the question with entity framework, I assume you want to do it in your data layer or close to DB. There's a number of ways for doing this.
You could override SaveChanges() in your context. This will move the logic away from the model, but still ensure that the correct value is saved. Also, if you want it on several entities you can use an interface. When it's an interface you can do it for several of your entities without any duplicate code, as long as it's the same property. Otherwise you would need an attribute and reflection. Reusability is pretty high, but it adds some overhead to your SaveChanges().
public class CustomerEntity()
{
public string Name {get;set;}
}
public MyCustomContext : DbContext
{
// Other stuff...
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries<CustomerEntity>())
{
if (entry.State == EntityState.Modified || entry.State == EntityState.Added)
{
// Possibly check for null or if it's changed at all.
entry.Entity.Name = entry.Entity.Name.ToUpper();
}
}
return base.SaveChanges();
}
}
And with an interface:
public interface INameIsAlwaysUpperCase
{
string Name {get;set;}
}
public MyCustomContext : DbContext
{
// Other stuff...
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries<INameIsAlwaysUpperCase>())
{
if (entry.State == EntityState.Modified || entry.State == EntityState.Added)
{
// Possibly check for null or if it's changed at all.
entry.Entity.Name = entry.Entity.Name.ToUpper();
}
}
return base.SaveChanges();
}
}
You can add a custom validation. This will throw exception if it's not saved correctly. That way you can move the responsibility to the consumer of the model. However, depending on your scenario, you might not want to throw an exception. This is my favourite since it forces the consumer to do it the right way. As per comments, why throw when you can silently convert it? Yes, it's a valid question. For me it's about forcing the consumer of the data layer to use it correctly, and not let the daya layer decide what to to with the data. I personally don't like it when the business layer asks the data layer to save one thing, and then the data layer saves another thing. If lower case isn't a valid option, then it shouldn't be saved. I don't think it's much more different from using [Required]. But it's really about context and what works in your particular case.
public class CustomerEntity() : IValidatableObject
{
public string Name {get;set;}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// Possibly check for null here as well...
if (this.Name.ToUpper() != this.Name)
{
yield return new ValidationResult("You need to save as upper!");
}
}
}
Use a property that manages this for you. This may be the simplest solution, even if I like to keep my entities "clean". It's absolutely the solution that will require least effort. However, the reusability is low, and what if you use your entitites all over the application and want the value to be lower case until it's actually saved? That's not possible. But, again, I think it comes down to your particular situation. If you want the value to be upper case even before you save it, this is probably the best solution!
public class CustomerEntity()
{
string _name;
public string Name
{
get { return _name; }
set { _name = value.ToUpper(); } // Check for null ?
}
}
Do it when saving. This moves the logic to when you're saving your entity. This is probably the least preferable option, since the reusability is non-existing. What happens in Update()? However, the OP specifically states "When I add a new row", so it may only be applicable when adding new entities. And in that case it could very well be the most prefered choice since it allows updates to have lower case. But it would have to depend on the use case.
public void AddCustomer(string name)
{
var customer = new CustomerEntity
{
Name = name.ToUpper()
};
_context.Customers.Add(customer);
}
Just use properties. If your model is as below:
public class MyModel
{
public int Id { get; set; }
public string Description { get; set; }
public string LanguageCode { get; set; }
public string FiledName { get; set; }
}
Then, change it to:
public class MyModel
{
private string fieldName;
public int Id { get; set; }
public string Description { get; set; }
public string LanguageCode { get; set; }
public string FiledName
{
get { return filedName; }
set
{
if(!string.IsNullOrEmpty(value))
fieldName = value.ToUpper();
else
fieldName = value;
}
}
}
Try this.
public string FiledName
{
get { return filedName; }
set
{
filedName = !string.IsNullOrEmpty( value ) ? value.ToUpper() : value;
}
}
Using a ValueConverter on the Property could be an effective way to do this.
public class YourDbContext : DbContext
{
public YourDbContext(DbContextOptions<YourDbContext> options)
: base(options)
{
}
public DbSet<Row> Rows { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
var converter = new ValueConverter<string, string>(
v => v.ToUpper(), // writing
v => v
);
// just one property
modelBuilder.Entity<Row>()
.Property(x => x.Column)
.HasConversion(converter);
// all of the string properties
foreach (var entityType in builder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
if (property.ClrType == typeof(string))
{
builder.Entity(entityType.Name)
.Property(property.Name)
.HasConversion(converter);
}
}
}
}
}
It's also possible to use a Custom Attribute :
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class Standardized : Attribute
{}
Then decorate properties inside your model :
public class MyModel
{
public string Id{ get; set; }
[Required]
[Standardized]
public string Description { get; set; }
}
Taken from #smoksnes accepted answer, inside your DbContext class, override SaveChanges(), SaveChangesAsync() (EF Core 5.x) and add a private method using reflection to obtain decorated properties and apply transformations, like this :
public override int SaveChanges()
{
StandardizeBeforeSaving();
return base.SaveChanges();
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
StandardizeBeforeSaving();
return await base.SaveChangesAsync(cancellationToken);
}
private void StandardizeBeforeSaving()
{
foreach (var entry in ChangeTracker.Entries())
{
if (entry.State == EntityState.Modified || entry.State == EntityState.Added)
{
var properties = entry.Entity
.GetType()
.GetProperties()
.Where(prop => Attribute.IsDefined(prop, typeof(Standardized)) && prop.PropertyType == typeof(string));
foreach (var property in properties)
{
var value = entry.CurrentValues[property.Name]?.ToString() ?? string.Empty;
entry.CurrentValues[property.Name] = value.Standardize();
}
}
}
}
Just be aware that reflection could be slower than other techniques presented in accepted answer. But for most scenarios (ie. user updates or creates couple of entities with not that many properties) it should be fine.

ValidateProperties code

Hi all i am using mvvmcross and portable class libraries , so i cannot use prism or componentmodel data annotations, to validate my classes. basically i have a modelbase that all my models inherit from.
My validate code below is horribly broken, basically im looking for the code that data annotations uses to iterate thru all the properties on my class that is inheriting the base class ,
i have written various attributes that are there own validators inheriting from "validatorBase" which inherits from attribute. i just cannot for the life of me figure out thecode that says ... ok im a class im going to go through all the properties in me that have an attribute of type ValidatorBase and run the validator. my code for these are at the bottom
public class ModelBase
{
private Dictionary<string, IEnumerable<string>> _errors;
public Dictionary<string, IEnumerable<string>> Errors
{
get
{
return _errors;
}
}
protected virtual bool Validate()
{
var propertiesWithChangedErrors = new List<string>();
// Get all the properties decorated with the ValidationAttribute attribute.
var propertiesToValidate = this.GetType().GetRuntimeProperties()
.Where(c => c.GetCustomAttributes(typeof(ValidatorBase)).Any());
foreach (PropertyInfo propertyInfo in propertiesToValidate)
{
var propertyErrors = new List<string>();
TryValidateProperty(propertyInfo, propertyErrors);
// If the errors have changed, save the property name to notify the update at the end of this method.
bool errorsChanged = SetPropertyErrors(propertyInfo.Name, propertyErrors);
if (errorsChanged && !propertiesWithChangedErrors.Contains(propertyInfo.Name))
{
propertiesWithChangedErrors.Add(propertyInfo.Name);
}
}
// Notify each property whose set of errors has changed since the last validation.
foreach (string propertyName in propertiesWithChangedErrors)
{
OnErrorsChanged(propertyName);
OnPropertyChanged(string.Format(CultureInfo.CurrentCulture, "Item[{0}]", propertyName));
}
return _errors.Values.Count == 0;
}
}
here is my validator
public class BooleanRequired : ValidatorBase
{
public override bool Validate(object value)
{
bool retVal = true;
retVal = value != null && (bool)value == true;
var t = this.ErrorMessage;
if (!retVal)
{
ErrorMessage = "Accept is Required";
}
return retVal;
}
}
and here is an example of its usage
[Required(ErrorMessage = "Please enter the Amount")]
public decimal Amount
{
get { return _amount; }
set { _amount = value; }//SetProperty(ref _amount, value); }
}

DefaultValue not working as expected when used with Custom Type Convertor

Per the below sample code posted I am able to see the values as Risk and Default in the dropdown.
But since I have a setting [DefaultValue("Risk")] above the property named "DummyProperty" I would expect the Risk value selected in the Property Grid Dropdown. But it’s not happening. What am I missing here?
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
string sDummy;
[DefaultValue("Risk")]
[Category("Test")]
[ParamDesc("SystemType")]
[TypeConverter(typeof(PropertyGridTypeConverter))]
public String DummyProperty
{
get { return sDummy; }
set { sDummy = value; }
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class ParamDesc : Attribute
{
public ParamDesc(string PD)
{ PropDesc = PD; }
public string PropDesc
{ get; set; }
}
class PropertyGridTypeConverter : TypeConverter
{
List<string> lst = new List<string>();
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
if (context != null)
{
AttributeCollection ua = context.PropertyDescriptor.Attributes;
ParamDesc cca = (ParamDesc)ua[typeof(ParamDesc)];
switch (cca.PropDesc)
{
case "SystemType":
lst = new List<string> {"Risk", "Default"};
break;
case "DateType":
lst = new List<string> {"Daily", "Monthly"};
break;
}
}
lst.Sort();
return new StandardValuesCollection(lst);
}
}
Somewhat confusingly, the DefaultValue custom attribute isn't used to set default values on properties like you want. In fact, it isn't directly used by the runtime at all. It's intended instead for use by the Visual Studio designer.
You'll probably just want to initialize the value elsewhere (such as in the UserControl1 constructor).
More information here:
.Net DefaultValueAttribute on Properties

Categories

Resources