Is there a proper way to access other form fields from inside a validator?
Is there another solution than:
context.getViewRoot().findComponent("formid:exampleField:example")?
f.e I want to validate a city field inside a custom validator and checking if country is US.
Re-read your question and I am going to interpret it as this:
"You would like to write a custom validator that checks that if a city field exists, the country field is equal to 'US'"
So, I would look at going about this in the following fashion:
First create a validator interface:
#Documented
#ValidatorClass(value=CountryEqualsUSValidator.class)
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface CountryEqualsUS {
String message() default "The country should be US for the city provided";
}
Then create a validator class:
public class CountryEqualsUSValidator implements Validator<CountryEqualsUS> {
public void initialize(CountryEqualsUS arg0) {
}
public boolean isValid(Object value) {
if(value != null && value instanceof YourBeanClass) {
YourBeanClass yourBeanClass = (YourBeanClass) value;
if(/*some test logic here*/) {
return true;
else {
return false;
}
}
return false;
}
}
Then on the class that you want to validate:
#CountryEqualsUS
public class YourBeanClass {
...
}
Then, finally, on your controller/action class, when the form is submitted, the city is a value for which you want to check the country, add this method and call it:
public boolean doValidation(YourBeanClass yourBeanClass) {
ClassValidator requestValidator = new ClassValidator(yourBeanClass.getClass());
InvalidValue[] validationMessages = requestValidator.getInvalidValues(yourBeanClass);
if (validationMessages != null && validationMessages.length > 0) {
for (int i = 0; i < validationMessages.length; i++) {
//Add a validation message to be displayed to the user
}
return false;
}
return true;
}
Related
I´m learning how to use CustomScalar in graphql-dotnet.
I have a tinyint column in my table and from what I have read, I´m supposed to use byte on this column in C#. After research I found out that I need to create a ByteGraphType, but I´m having trouble doing that.
I got the ByteGraphType example from this link https://github.com/graphql-dotnet/graphql-dotnet/issues/458, so I think it will work.
With this code, I can query the table, however, my mutation is not working. I didn´t find an example to demonstrate how the mutation would look like with a byte column. I tried as is stated in my code example, but in this line (var avaliacao = context.GetArgument("avaliacao");), my argument avaliacao.Nota is returning null and I´m not sure on how to proceed.
Can someone help me?
Thank you
THAT´S MY CODE
//Model
[Column("nota")]
public byte Nota { get; set; }
//Type
Field<ByteGraphType>("Nota", resolve: context => context.Source.Nota);
//InputType
Field<ByteGraphType>("nota");
//Query
Field<ListGraphType<AvaliacaoType>>(
"avaliacoes",
resolve: context => contextServiceLocator.AvaliacaoRepository.All());
//Mutation
Field<AvaliacaoType>(
"createAvaliacao",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<AvaliacaoInputType>> { Name = "avaliacao" }
),
resolve: context =>
{
var schema = new Schema();
schema.RegisterValueConverter(new ByteValueConverter());
var avaliacao = context.GetArgument<Avaliacao>("avaliacao");
avaliacao.Nota.AstFromValue(schema, new ByteGraphType());
return contextServiceLocator.AvaliacaoRepository.Add(avaliacao);
});
//ByteGraphType
using GraphQL.Language.AST;
using GraphQL.Types;
using System;
namespace Api.Helpers
{
public class ByteGraphType : ScalarGraphType
{
public ByteGraphType()
{
Name = "Byte";
}
public override object ParseLiteral(IValue value)
{
var byteVal = value as ByteValue;
return byteVal?.Value;
}
public override object ParseValue(object value)
{
if (value == null)
return null;
try
{
var result = Convert.ToByte(value);
return result;
}
catch (FormatException)
{
return null;
}
}
public override object Serialize(object value)
{
return ParseValue(value).ToString();
}
public class ByteValueConverter : IAstFromValueConverter
{
public bool Matches(object value, IGraphType type)
{
return value is byte;
}
public IValue Convert(object value, IGraphType type)
{
return new ByteValue((byte)value);
}
}
public class ByteValue : ValueNode<byte>
{
public ByteValue(byte value)
{
Value = value;
}
protected override bool Equals(ValueNode<byte> node)
{
return Value == node.Value;
}
}
}
}
What I need is to be able to save a record of a table that has a tinyint column. If I change the type in my code to int, I can mutate, but can´t query.
I changed my CustomScalar and it worked:
using GraphQL.Language.AST;
using GraphQL.Types;
using System;
namespace Api.Helpers
{
public class ByteGraphType : ScalarGraphType
{
public ByteGraphType()
{
Name = "Byte";
Description = "ByteGraphType";
}
/// <inheritdoc />
public override object Serialize(object value)
{
return ParseValue(value).ToString();
}
/// <inheritdoc />
public override object ParseValue(object value)
{
byte result;
if (byte.TryParse(value?.ToString() ?? string.Empty, out result))
{
return result;
}
return null;
}
/// <inheritdoc />
public override object ParseLiteral(IValue value)
{
if (value is StringValue)
{
return ParseValue(((StringValue)value).Value);
}
return null;
}
}
}
I'm working with EF 4.5 and DbContext. At business rules layer level, I should implement checks to avoid change entity value properties in some entity scenarios. Sample: StartProjecteDate should be readonly if ProjectIsStarted but not in other status.
I follow DRY principle, for this reason, I should be able to inspect readonly properties list from context and also from UI.
My question:
Is there a DataAnnotation validator that can dynamically set properties as readonly?
(and if not, is there a different / better solution to this problem?)
Notice than I'm working with Web Forms (and Telerik) architecture, a clean and elegant pattern will be welcome.
I'm trying to set and get at run time EditableAttribute as Jesse Webb explains, but I'm not able to get dataannotation attributes from property, my code:
<EditableAttribute(False)>
<MaxLength(400, ErrorMessage:="Màxim 400 caracters")>
Public Property NomInvertebrat As String
Edited Nov 8 2013 after digging docs, it seems that dataanottions if for class but for instance object itself. Perhaps an iReadonlyableProperties interface may be a way.
I have a class containing extension methods that lets me read data annotations like this:
int maxRefLen = ReflectionAPI.GetProperty<Organisation, String>(x => x.Name)
.GetAttribute<StringLengthAttribute>()
.GetValueOrDefault(x => x.MaximumLength, 256);
So if you use it you should be able to do get the value of the EditableAttribute like this:
bool isEditable = ReflectionAPI.GetProperty<Foo, String>(x => x.NomInvertebrat)
.GetAttribute<EditableAttribute>()
.GetValueOrDefault(x => x.AllowEdit, true);
As for setting the data annotations at run-time, I haven't done it myself but I have read that there is a solution here: Setting data-annotations at runtime
Getting a list of all data annotations of a particular type I think would entail reading the entity framework metadata. Again I haven't tried this.
If you add that together I personally think it feels clunky rather than elegant, but you have asked for a solution using DataAnnotations and something more elegant would probably mean getting into your architecture.
I would be inclined to do this:
public bool StartDateIsReadOnly
{
//use this property client-side to disable the input
get{ return Project.IsStarted;}
}
//Implement IValidatable object to do server side validation
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext
{
bool startdateIsChanged = // I'll leave you to work out this bit
var results = new List<ValidationResult>();
if(StartDateIsReadOnly && startdateIsChanged)
results.Add(new ValidationResult("Start Date cannot be changed after project is started");
}
Here is the ReflectionAPI class:
Please note that the class includes part of a hack that #JonSkeet posted and described as "evil". I personally think this bit ain't so bad, but you should read the following references:
Override a generic method for value types and reference types.
Evil code - overload resolution workaround
public static class ReflectionAPI
{
public static int GetValueOrDefault<TInput>(this TInput a, Func<TInput, int> func, int defaultValue)
where TInput : Attribute
//Have to restrict to struct or you get the error:
//The type 'R' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable<T>'
{
if (a == null)
return defaultValue;
return func(a);
}
public static Nullable<TResult> GetValueOrDefault<TInput, TResult>(this TInput a, Func<TInput, TResult> func, Nullable<TResult> defaultValue)
where TInput : Attribute
where TResult : struct
//Have to restrict to struct or you get the error:
//The type 'R' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable<T>'
{
if (a == null)
return defaultValue;
return func(a);
}
//In order to constrain to a class without interfering with the overload that has a generic struct constraint
//we need to add a parameter to the signature that is a reference type restricted to a class
public class ClassConstraintHack<T> where T : class { }
//The hack means we have an unused parameter in the signature
//http://msmvps.com/blogs/jon_skeet/archive/2010/11/02/evil-code-overload-resolution-workaround.aspx
public static TResult GetValueOrDefault<TInput, TResult>(this TInput a, Func<TInput, TResult> func, TResult defaultValue, ClassConstraintHack<TResult> ignored = default(ClassConstraintHack<TResult>))
where TInput : Attribute
where TResult : class
{
if (a == null)
return defaultValue;
return func(a);
}
//I don't go so far as to use the inheritance trick decribed in the evil code overload resolution blog,
//just create some overloads that take nullable types - and I will just keep adding overloads for other nullable type
public static bool? GetValueOrDefault<TInput>(this TInput a, Func<TInput, bool?> func, bool? defaultValue)
where TInput : Attribute
{
if (a == null)
return defaultValue;
return func(a);
}
public static int? GetValueOrDefault<TInput>(this TInput a, Func<TInput, int?> func, int? defaultValue)
where TInput : Attribute
{
if (a == null)
return defaultValue;
return func(a);
}
public static T GetAttribute<T>(this PropertyInfo p) where T : Attribute
{
if (p == null)
return null;
return p.GetCustomAttributes(false).OfType<T>().LastOrDefault();
}
public static PropertyInfo GetProperty<T, R>(Expression<Func<T, R>> expression)
{
if (expression == null)
return null;
MemberExpression memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
return null;
return memberExpression.Member as PropertyInfo;
}
}
.NET allows you to dynamically change structure of Class by implementing System.ComponentModel.ICustomTypeDescriptor. Most serializers support this interface.
// Sample Serialization
foreach(PropertyDescriptor p in TypeDescriptor.GetProperties(obj)){
string name = p.PropertyName;
object value = p.GetValue(obj);
}
Internally TypeDescriptor uses Reflection, but the implementation allows us to override reflection attributes easily.
Here are three steps of implementation,
// Implement System.ComponentModel.ICustomTypeDescriptor Interface on
// your Entity
public class MyEntity: System.ComponentModel.ICustomTypeDescriptor
{
....
// most methods needs only call to default implementation as shown below
System.ComponentModel.AttributeCollection
System.ComponentModel.ICustomTypeDescriptor.GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
string System.ComponentModel.ICustomTypeDescriptor.GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
string System.ComponentModel.ICustomTypeDescriptor.GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
System.ComponentModel.TypeConverter System.ComponentModel.ICustomTypeDescriptor.GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
System.ComponentModel.EventDescriptor System.ComponentModel.ICustomTypeDescriptor.GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
System.ComponentModel.PropertyDescriptor System.ComponentModel.ICustomTypeDescriptor.GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this, true);
}
object System.ComponentModel.ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
return TypeDescriptor.GetProperties(this, attributes, true);
}
object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd)
{
return this;
}
// The Only method that needs different implementation is below
System.ComponentModel.PropertyDescriptorCollection
System.ComponentModel.ICustomTypeDescriptor.GetProperties()
{
// ... you are supposed to create new instance of
// PropertyDescriptorCollection with PropertyDescriptor
PropertyDescriptorCollection pdc = new PropertyDescriptorCollection();
foreach(PropertyDescriptor p in TypeDescriptor.GetProperties(this,true)){
// if readonly..
AtomPropertyDescriptor ap = new AtomPropertyDescriptor(p, p.Name);
// or
AtomPropertyDescriptor ap = new AtomPropertyDescriptor(p, p.Name,
true,
new XmlIgnoreAttribute(),
new ScriptIgnoreAttribute(),
new ReadOnlyAttribute());
pdc.Add(ap);
}
return pdc;
}
}
// And here is the AtomPropertyDescriptorClass
public class AtomPropertyDescriptor : PropertyDescriptor
{
PropertyDescriptor desc;
bool? readOnly = null;
public AtomPropertyDescriptor(PropertyDescriptor pd, string name,
bool? readOnly, params Attribute[] attrs) :
base(name, attrs)
{
desc = pd;
this.readOnly = readOnly;
}
public override bool CanResetValue(object component)
{
return desc.CanResetValue(component);
}
public override Type ComponentType
{
get
{
return desc.ComponentType;
}
}
public override object GetValue(object component)
{
return desc.GetValue(component);
}
public override bool IsReadOnly
{
get
{
if (readOnly.HasValue)
return readOnly.Value;
return desc.IsReadOnly;
}
}
public override Type PropertyType
{
get { return desc.PropertyType; }
}
public override void ResetValue(object component)
{
desc.ResetValue(component);
}
public override void SetValue(object component, object value)
{
desc.SetValue(component, value);
}
public override bool ShouldSerializeValue(object component)
{
return desc.ShouldSerializeValue(component);
}
}
I think what you are looking for is a custom Annotation Attribute like this:
<DisableEditAttribute(this.IsProjectStarted)>
Public Property NomInvertebrat As String
public override bool IsValid(bool value)
{
bool result = true;
// Add validation logic here.
if(value)
{
//Compare Current Value Against DB Value.
}
return result;
}
See MSDN: http://msdn.microsoft.com/en-us/library/cc668224(v=vs.98).aspx
I have an MVC application which I have added custom cross-field validation. The cross-field validation isn't configured to be client-side however when I tab through my fields IE is throwing the following error "$.validator.method[...] is null or not an object" from within jquery.validate.js. I have attached the full version so I can debug what's going on and it seems to be trying to fire my "mandatoryif" custom validation below on the client-side and then throwing the error at the following line:
var result = $.validator.methods[method].call( this, element.value.replace(/\r/g, ""), element, rule.parameters );
Any ideas why it is trying to do this when the "mandatoryif" validation hasn't been added client-side?
I have also updated to the latest version as I read it could be the version of jQuery.validate but this didn't fix the issue either.
Here is my custom validation:
Attribute
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class MandatoryIfAttribute : ValidationAttribute, ICrossFieldValidationAttribute
{
public string OtherProperty { get; set; }
public bool IsValid(ControllerContext controllerContext, object model, ModelMetadata modelMetadata)
{
if (model == null)
{
throw new ArgumentNullException("model");
}
// Find the value of the other property.
var propertyInfo = model.GetType().GetProperty(OtherProperty);
if (propertyInfo == null)
{
throw new InvalidOperationException(string.Format("Couldn't find {0} property on {1}.",
OtherProperty, model));
}
var otherValue = propertyInfo.GetGetMethod().Invoke(model, null);
if (modelMetadata.Model == null)
{
modelMetadata.Model = string.Empty;
}
if (otherValue == null)
{
otherValue = string.Empty;
}
return (String.IsNullOrEmpty(modelMetadata.Model.ToString()) && (String.IsNullOrEmpty(otherValue.ToString()) || otherValue.ToString() == "0")) || (!String.IsNullOrEmpty(modelMetadata.Model.ToString()) && String.IsNullOrEmpty(otherValue.ToString())) || (!String.IsNullOrEmpty(modelMetadata.Model.ToString()) && !String.IsNullOrEmpty(otherValue.ToString()));
}
public override bool IsValid(object value)
{
// Work done in other IsValid
return true;
}
Validator
public class MandatoryIfValidator : CrossFieldValidator<MandatoryIfAttribute>
{
public MandatoryIfValidator(ModelMetadata metadata, ControllerContext controllerContext,
MandatoryIfAttribute attribute) :
base(metadata, controllerContext, attribute)
{
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
var rule = new ModelClientValidationRule
{
ValidationType = "mandatoryif",
ErrorMessage = Attribute.FormatErrorMessage(Metadata.PropertyName),
};
rule.ValidationParameters.Add("otherProperty", Attribute.OtherProperty);
return new[] { rule };
}
Appreciate any help on this.
I have managed to find the answer to my query. Because some of my custom validation was doing lookups to the db I didn't want the overhead of this being done client-side so there was no client-side code. However, I didn't realise that by adding
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(MandatoryIfAttribute),
typeof(MandatoryIfValidator));
in Global.asax that it was in affect adding this client-side. Of course because there was no code to process client-side it was throwing the validaiton error.
I'm just getting to grips with custom validation attributes, and I'm trying to write a custom validation attirbute which will be placed at class level to validate against multiple properties of my model.
I can access all properties on my model, and I want to be able to check for multiple conditions in my IsValid overload, and report on them, having different error messages as follows (simplistic example).
public override bool IsValid(object value)
{
var model = (MyObject) value;
//if this value is set, I don't want to do anything other checks
if (model.Prop3)
{
return true;
}
if (model.Prop1 == "blah" && model.Prop2 == 1)
{
ErrorMessage = "you can't enter blah if prop 2 equals 1";
return false;
}
if(model.Prop1 == "blah blah" && model.Prop2 == 2)
{
ErrorMessage = "you can't enter blah blah if prop 2 equals 2";
return false;
}
return true;
}
But when I do this I get an exception on the first time ErrorMessage is referenced "Cannot set property more than once.
Now I could split up my custom attribute into multiple custom attributes, but hoped there would be a way to do it in one, otherwise, I'll be repeating my "catch all" in each
//if this value is set, I don't want to do anything other checks
if (model.Prop3)
{
return true;
}
I've had a search already, but couldn't find anything, so apologies if I am missing anything obvious.
thanks in advance!
In MVC4 you can override IsValid to return different messages as the ValidationResult
public class StrongPasswordAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
{
if (value == null)
return new ValidationResult("Password is required");
var val = value.ToString();
if (!Regex.Match(val, #"^(?=.*[a-z]).{0,}$").Success)
{
return new ValidationResult("Password must contain at least one lower case letter");
}
if (!Regex.Match(val, #"^(?=.*[A-Z]).{0,}$").Success)
{
return new ValidationResult("Password must contain at least one UPPER case letter");
}
if (!Regex.Match(val, #"^(?=.*\d).{0,}$").Success)
{
return new ValidationResult("Password must contain at least one number");
}
return ValidationResult.Success;
}
}
Interesting question! I can think of two work-arounds to this. So not proper solutions based on what you want but they might help to re-use your code. Cant you create a CustomAttribute abstract class called MyCustomAttribute (or something) that overrides IsValid in the following way:
public override bool IsValid(object value)
{
var model = (MyObject) value;
//if this value is set, I don't want to do anything other checks
if (model.Prop3)
{
return true;
}
CustomValidate(model);
}
CustomValidate(MyObject model) is your abstract method then, you can write multiple custom attribute classes that extend MyCustomAttribute and purely need to implement the validation logic for A particular scenario.
So you can have two classes:
public class BlahCustomAttribute : MyCustomAttribute
{
public override Boolean CustomValidate(MyObject obj)
{
if (model.Prop1 == "blah" && model.Prop2 == 1)
{
ErrorMessage = "you can't enter blah if prop 2 equals 1";
return false;
}
}
}
public class BlahBlahCustomAttribute : MyCustomAttribute
{
public override Boolean CustomValidate(MyObject obj)
{
if (model.Prop1 == "blah" && model.Prop2 == 1)
{
ErrorMessage = "you can't enter blah blah if prop 2 equals 1";
return false;
}
}
}
Hope this helps - not exactly what you wanted but will do the job and its clean as well.
The other solution is to comma-separate the error messages in the ErrorMessage property and handle it in the front-end (but I would go with the first approach).
Does the ASP.NET MVC 2 Default View Model Binding support binding a multi-value cookie to a custom object? Before I write a custom Value Provider, I would like to be sure that the functionality didn't already exist.
So given an action like:
public ActionResult SomeAction(CustomObject foo)
where CustomObject is something like:
public class CustomObject
{
public string Name { get; set; }
public int Rank { get; set; }
}
and a cookie that is part of the request like:
foo=Name=John&Rank=10
Could I get the Default View Model Binding to map the cookie to the parameter with some clever tweaks to the naming of the cookie or cookie values like posting "foo.Name=John" and "foo.Rank=10" would do?
Well, there's one way to do it would be to implement IModelBinder
public class CustomObjectModelBinder : IModelBinder {
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
HttpCookie c = controllerContext.HttpContext.Request.Cookies["foo"]
CustomObject value = new CustomObject() {
foo.Name = c.Values["Name"],
foo.Rank = c.Values["Rank"]
}
return CustomObject
}
}
Then just add this to your Application_Start()
ModelBinders.Binders.Add(typeof(CustomObject), new CustomObjectModelBinder());
you can add the cookie object to any action as far as i know and it will attempt to bind it for you
In the end I created something to do this. Based on the work posted by Mehdi Golchin, I created a value provider that allows this kind of binding to happen.
For those interrested, the following are the custom changes I made to Mehdi's work linked above. See the link for full details on implementation. This doesn't support binding to nested objects (e.g., Foo.Cell.X) because I didn't need that level of complexity, but it would be possible to implement with a bit of recursion.
protected virtual bool ContainsPrefix(string prefix)
{
try
{
var parts = prefix.Split(new char[] { '.' }, 2, StringSplitOptions.RemoveEmptyEntries);
switch (parts.Length)
{
case 0:
return false;
case 1:
return this._context.HttpContext.Request.Cookies.AllKeys.Contains(parts[0]);
default:
var cookie = this._context.HttpContext.Request.Cookies[parts[0]];
if (cookie == null) { return false; }
return cookie.Values.AllKeys.Contains(parts[1]);
}
}
catch (Exception ex)
{
ExceptionPolicy.HandleException(ex, "Controller Policy");
return false;
}
}
protected virtual ValueProviderResult GetValue(string key)
{
try
{
var parts = key.Split(new char[] { '.' }, 2, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length < 2) { return null; }
var cookie = this._context.HttpContext.Request.Cookies[parts[0]];
if (cookie == null) { return null; }
var value = cookie.Values[parts[1]];
if (value == null) { return null; }
return new ValueProviderResult(value, value, CultureInfo.CurrentCulture);
}
catch (Exception ex)
{
ExceptionPolicy.HandleException(ex, "Controller Policy");
return null;
}
}