MVC DataAnnotation RequiredIfNot - c#

I would need help to ensure CustomerCode has value only if UserTypeID is not 1
public class FilteringViewModel
{
[Required]
public int? UserID { get; set; }
[Required]
public string UserTypeID { get; set; }
[RequiredIf("UserTypeID", "1")]
public string EmployeeCode { get; set; }
[RequiredIf("UserTypeID", "!1")]
public string CustomerCode { get; set; }
}
public class RequiredIfAttribute : ValidationAttribute
{
RequiredAttribute _innerAttribute = new RequiredAttribute();
public string _dependentProperty { get; set; }
public object _targetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this._dependentProperty = dependentProperty;
this._targetValue = targetValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var field = validationContext.ObjectType.GetProperty(_dependentProperty);
if (field != null)
{
var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
if ((dependentValue == null && _targetValue == null) || (dependentValue.Equals(_targetValue)))
{
if (!_innerAttribute.IsValid(value))
{
string name = validationContext.DisplayName;
string specificErrorMessage = ErrorMessage;
if (string.IsNullOrEmpty(specificErrorMessage))
specificErrorMessage = $"{name} is required.";
return new ValidationResult(specificErrorMessage, new[] { validationContext.MemberName });
}
}
return ValidationResult.Success;
}
else
{
return new ValidationResult(FormatErrorMessage(_dependentProperty));
}
}
}
EmployeeCode is required if UserTypeID is 1 (this part is working fine)
CustomerCode is required if UserTypeID is not 1 (this part is not working)

As per explain by Yong Shun, the logic does not affected by "!". I have change the IsValid() function as per below and now its working:
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var field = validationContext.ObjectType.GetProperty(_dependentProperty);
if (field != null)
{
var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
if (
(dependentValue == null && _targetValue == null)
|| (!_targetValue.ToString().StartsWith("!") && dependentValue.Equals(_targetValue))
|| (_targetValue.ToString().StartsWith("!") && !dependentValue.Equals(_targetValue.ToString().Substring(1)))
)
{
//this statement means that we will proceed to do the validation
if (!_innerAttribute.IsValid(value))
{
string name = validationContext.DisplayName;
string specificErrorMessage = ErrorMessage;
if (string.IsNullOrEmpty(specificErrorMessage))
specificErrorMessage = $"{name} is required.";
return new ValidationResult(specificErrorMessage, new[] { validationContext.MemberName });
}
}
return ValidationResult.Success;
}
else
{
return new ValidationResult(FormatErrorMessage(_dependentProperty));
}
}

Related

Where clause conversion error when filtering records using EF Core 2.0

I'm getting a list of records and filtering according to the given parameter. The problem is that an error is appearing that I don't know about (as shown in the image). There are two classes: PesoaDocumento and DocumentTipo.... When you get a list of documents, you must bring the types of related documents.
What is wrong?
public IQueryable<PessoaDocumento> GetPaginated(string filter, int initialPage, int pageSize, out int totalRecords, out int recordsFiltered, int pessoaId, bool #readonly = false)
{
var data = DbSet
.Include(x => x.DocumentoTipo)
.Where(x => x.PessoaId == pessoaId)
.AsNoTracking().AsQueryable();
totalRecords = data.Count();
if (!string.IsNullOrEmpty(filter))
{
data = data.Where(x =>
!string.IsNullOrEmpty(x.NumeroDocumento) ? x.NumeroDocumento.Contains(filter.ToUpper()) : false
|| x.DocumentoTipo != null ? x.DocumentoTipo.Descricao.Contains(filter.ToUpper()) : false
|| x.DocumentoTipo != null ? x.DocumentoTipo.Sigla.Contains(filter.ToUpper()) : false
);
}
recordsFiltered = data.Count();
if (recordsFiltered == 0)
totalRecords = 0;
data = data
.Skip(initialPage)
.Take(pageSize);
return data;
}
public class PessoaDocumento : Entity
{
public int Id { get; set; }
public int DocumentoTipoId { get; set; }
private string numeroDocumento;
public string NumeroDocumento
{
get { return numeroDocumento; }
set
{
if (value != null)
{
numeroDocumento = value.Trim().ToUpper();
}
}
}
public virtual DocumentoTipo DocumentoTipo { get; private set; }
}
public class DocumentoTipo : Entity
{
private string codigoControle;
public string CodigoControle
{
get { return codigoControle; }
set
{
if (value != null)
codigoControle = value.ToUpper().Trim();
else
codigoControle = value;
}
}
private string descricao;
public string Descricao
{
get { return descricao; }
set
{
if (value != null)
descricao = value.ToUpper().Trim();
else
descricao = value;
}
}
private string sigla;
public string Sigla
{
get { return sigla; }
set
{
if (value != null)
sigla = value.ToUpper().Trim();
else
sigla = value;
}
}
public virtual ICollection<PessoaDocumento> PessoasDocumentos { get; private set; }
}
public abstract class Entity
{
//public Guid Id { get; protected set; }
public int Id { get; protected set; }
public override bool Equals(object obj)
{
var compareTo = obj as Entity;
if (ReferenceEquals(this, compareTo)) return true;
if (ReferenceEquals(null, compareTo)) return false;
return Id.Equals(compareTo.Id);
}
public static bool operator ==(Entity a, Entity b)
{
if (ReferenceEquals(a, null) && ReferenceEquals(b, null))
return true;
if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
return false;
return a.Equals(b);
}
public static bool operator !=(Entity a, Entity b)
{
return !(a == b);
}
public override int GetHashCode()
{
return (GetType().GetHashCode() * 907) + Id.GetHashCode();
}
public override string ToString()
{
return GetType().Name + " [Id=" + Id + "]";
}
}
error - here
your problem is here
!string.IsNullOrEmpty(x.NumeroDocumento) ?
you can't use any converting function in linq query. modifiy your this condition,
it will work

C# UniqueAttribute validation check DB and ignore old value

I create my own UniqueAttribute validation which check data in DB.
Problem is how to ignore own row.
So example:
Current email address: test#test.com Current address: Address1
now I change
Current address: Address2 Current email address: test#test.com ->
stays the same
I get message that this email already exist. How to remember old value?
Code:
#region
using Agado.Jobs.Infrastructure.ExtensionMethods;
using Agado.Jobs.StudentService.Web.Infrastructure.Utils;
using System;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Reflection;
#endregion
namespace Agado.Jobs.StudentService.Web.Infrastructure.Validators
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class UniqueAttribute : ValidationAttribute
{
public UniqueAttribute(Type dataContextType, Type entityType, string propertyName, string methodName)
{
DataContextType = dataContextType;
EntityType = entityType;
PropertyName = propertyName;
MethodName = methodName;
}
public Type DataContextType { get; private set; }
public Type EntityType { get; private set; }
public string PropertyName { get; private set; }
public string MethodName { get; private set; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null) return ValidationResult.Success;
var idValue = ValidationUtils.GetValue<int>(validationContext.ObjectInstance, "Id");
var repository = Jobs.Infrastructure.IoC.Resolve(DataContextType);
PropertyInfo propertyInfo = EntityType.GetProperty(PropertyName);
if (propertyInfo == null)
{
throw new Exception("PropertyInfo with name '{0}' is NULL".FormatWith(PropertyName));
}
var propertyType = propertyInfo.PropertyType;
if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
propertyType = Nullable.GetUnderlyingType(propertyType);
}
var args = new[] { Convert.ChangeType(value, propertyType) };
var data = repository.GetType().InvokeMember(MethodName, BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public, null, repository, args);
if (idValue != 0 && data != null)
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
else if (idValue == 0 && data != null)
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
else
{
return new RequiredAttribute().IsValid(value) ? ValidationResult.Success : new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return ValidationResult.Success;
}
public override string FormatErrorMessage(string name)
{
if (String.IsNullOrEmpty(ErrorMessage))
{
var genericMessageWithPlaceHolder = (string)Resources.ResourceManager.GetObject("PropertyUniqueDefaultError");
if (!string.IsNullOrEmpty(genericMessageWithPlaceHolder))
{
ErrorMessage = genericMessageWithPlaceHolder;
}
}
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, name);
}
}
}
Use:
[Unique(typeof(ICustomerRepository), typeof(customers), "Email", "GetByEmail")]
[Display(Name = Translations.Global.EMAIL)]
public string Email { get; set; }

RequiredIf data annotation with enums

I have created a custom RequiredIf validator like this:
public class RequiredIfValidator : ValidationAttribute, IClientValidatable
{
RequiredAttribute _innerAttribute = new RequiredAttribute();
public string _dependentProperty { get; set; }
public object _targetValue { get; set; }
public RequiredIfValidator(string dependentProperty, object targetValue)
{
this._dependentProperty = dependentProperty;
this._targetValue = targetValue;
}
public override string FormatErrorMessage(string name)
{
return string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, _dependentProperty);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var field = validationContext.ObjectInstance.GetType().GetProperty(_dependentProperty);
if (field != null)
{
var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
if ((dependentValue == null && _targetValue == null) ||(dependentValue.Equals(_targetValue)))
{
if (!_innerAttribute.IsValid(value))
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
}
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
rule.ValidationType = "requiredif";
rule.ValidationParameters["dependentproperty"] = _dependentProperty;
rule.ValidationParameters["targetvalue"] = _targetValue;
yield return rule;
}
}
I have an enum with various test types like this:
public enum TestTypes
{
Hair = 1,
Urine = 2
}
My ViewModel has some properties like this:
public class TestViewModel
{
public TestTypes TestTypeId {get; set;}
[RequiredIfValidator("TestTypeId", TestTypes.Hair)]
public string HairSpecimenId {get; set;}
}
My custom RequiredIfValidator is not working in this scinario. Is it because of the enum data type? Any way to achieve this with enums
You logic in the IsValid() does not appear to be correct. It should be
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
{
var otherProperty = validationContext.ObjectInstance.GetType().GetProperty(_dependentProperty);
var otherPropertyValue = otherProperty.GetValue(validationContext.ObjectInstance, null);
if (otherPropertyValue != null && otherPropertyValue.Equals(_targetValue ))
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
}
return ValidationResult.Success;
}

Emit mapper. Convert object to int

I have some trouble when I try to map object to int.
My classes and method where convert:
[Serializable]
public class ProfileProperty
{
public object PropertyValue { get; set; }
public bool IsVisible { get; set; }
public ProfileProperty(object value, bool isVisible = true)
{
this.PropertyValue = value;
this.IsVisible = isVisible;
}
public ProfileProperty()
{
this.IsVisible = true;
}
public T GetValue<T>()
{
return (T)this.PropertyValue;
}
public override string ToString()
{
if (this.PropertyValue != null)
{
return this.PropertyValue.ToString();
}
return string.Empty;
}
}
[Serializable]
public class ProfileProperty
{
public object PropertyValue { get; set; }
public bool IsVisible { get; set; }
public ProfileProperty(object value, bool isVisible = true)
{
this.PropertyValue = value;
this.IsVisible = isVisible;
}
public ProfileProperty()
{
this.IsVisible = true;
}
public T GetValue<T>()
{
return (T)this.PropertyValue;
}
public override string ToString()
{
if (this.PropertyValue != null)
{
return this.PropertyValue.ToString();
}
return string.Empty;
}
}
public static class Helper
{
public static ProfileModel PopulProfMod(Profile profile)
{
var mapper = ObjectMapperManager.DefaultInstance.GetMapper<Profile, ProfileModel>(new DefaultMapConfig()
.IgnoreMembers<Profile, ProfileModel>(new string[] { "GetValue", "ToString" }));
ProfileModel prof = new ProfileModel();
if (profile != null)
{
prof = mapper.Map(profile);
//prof.Age = (int)profile.Age.PropertyValue;
prof.Visibility = new List<string>();
}
//Some code
return prof;
}
}
When mapped property Age is 0 but profile:
Profile profile = new Profile()
{
Age = new ProfileProperty() { IsVisible = true, PropertyValue = 17 },
Comments = 2,
UserName = "Luck",
FirstName = new ProfileProperty() { IsVisible = false, PropertyValue = "Alex" }
};
You need a converter:
new DefaultMapConfig()
.IgnoreMembers<Profile, ProfileModel>(new string[] { "GetValue", "ToString" }))
.ConvertUsing<ProfileProperty, int>(s => (int)s.PropertyValue)

Opposite of [compare(" ")] data annotation in .net?

What is the opposite/negate of [Compare(" ")] data annotation" in ASP.NET?
i.e: two properties must hold different values.
public string UserName { get; set; }
[Something["UserName"]]
public string Password { get; set; }
You can use the [NotEqualTo] data annotation operator included in MVC Foolproof Validation. I used it right now and it works great!
MVC Foolproof is an open source library created by #nick-riggs and has a lot of available validators. Besides doing server side validation it also does client side unobtrusive validation.
Full list of built in validators you get out of the box:
Included Operator Validators
[Is]
[EqualTo]
[NotEqualTo]
[GreaterThan]
[LessThan]
[GreaterThanOrEqualTo]
[LessThanOrEqualTo]
Included Required Validators
[RequiredIf]
[RequiredIfNot]
[RequiredIfTrue]
[RequiredIfFalse]
[RequiredIfEmpty]
[RequiredIfNotEmpty]
[RequiredIfRegExMatch]
[RequiredIfNotRegExMatch]
This is the implementation (server side) of the link that #Sverker84 referred to.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class UnlikeAttribute : ValidationAttribute
{
private const string DefaultErrorMessage = "The value of {0} cannot be the same as the value of the {1}.";
public string OtherProperty { get; private set; }
public UnlikeAttribute(string otherProperty)
: base(DefaultErrorMessage)
{
if (string.IsNullOrEmpty(otherProperty))
{
throw new ArgumentNullException("otherProperty");
}
OtherProperty = otherProperty;
}
public override string FormatErrorMessage(string name)
{
return string.Format(ErrorMessageString, name, OtherProperty);
}
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
if (value != null)
{
var otherProperty = validationContext.ObjectInstance.GetType()
.GetProperty(OtherProperty);
var otherPropertyValue = otherProperty
.GetValue(validationContext.ObjectInstance, null);
if (value.Equals(otherPropertyValue))
{
return new ValidationResult(
FormatErrorMessage(validationContext.DisplayName));
}
}
return ValidationResult.Success;
}
}
Usage:
public string UserName { get; set; }
[Unlike("UserName")]
public string AlternateId { get; set; }
Details about this implementation, and how to implement it client-side can be found here:
http://www.devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-2
http://www.macaalay.com/2014/02/25/unobtrusive-client-and-server-side-not-equal-to-validation-in-mvc-using-custom-data-annotations/
The complete code for both server side and client side validation is as follows:
[AttributeUsage(AttributeTargets.Property)]
public class UnlikeAttribute : ValidationAttribute, IClientModelValidator
{
private string DependentProperty { get; }
public UnlikeAttribute(string dependentProperty)
{
if (string.IsNullOrEmpty(dependentProperty))
{
throw new ArgumentNullException(nameof(dependentProperty));
}
DependentProperty = dependentProperty;
}
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
if (value != null)
{
var otherProperty = validationContext.ObjectInstance.GetType().GetProperty(DependentProperty);
var otherPropertyValue = otherProperty.GetValue(validationContext.ObjectInstance, null);
if (value.Equals(otherPropertyValue))
{
return new ValidationResult(ErrorMessage);
}
}
return ValidationResult.Success;
}
public void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-unlike", ErrorMessage);
// Added the following code to account for the scenario where the object is deeper in the model's object hierarchy
var idAttribute = context.Attributes["id"];
var lastIndex = idAttribute.LastIndexOf('_');
var prefix = lastIndex > 0 ? idAttribute.Substring(0, lastIndex + 1) : string.Empty;
MergeAttribute(context.Attributes, "data-val-unlike-property", $"{prefix}{DependentProperty}");
}
private void MergeAttribute(IDictionary<string, string> attributes,
string key,
string value)
{
if (attributes.ContainsKey(key))
{
return;
}
attributes.Add(key, value);
}
}
Then include the following in JavaScript:
$.validator.addMethod('unlike',
function (value, element, params) {
var propertyValue = $(params[0]).val();
var dependentPropertyValue = $(params[1]).val();
return propertyValue !== dependentPropertyValue;
});
$.validator.unobtrusive.adapters.add('unlike',
['property'],
function (options) {
var element = $(options.form).find('#' + options.params['property'])[0];
options.rules['unlike'] = [element, options.element];
options.messages['unlike'] = options.message;
});
Usage is as follows:
public int FromId { get; set; }
[Unlike(nameof(FromId), ErrorMessage = "From ID and To ID cannot be the same")]
public int ToId { get; set; }
Use this in your get/set logic:
stringA.Equals(stringB) == false
In addition to solution given by #Eitan K, If you want to use other property's display name instead of other property's name, use this snippet:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class UnlikeAttribute : ValidationAttribute
{
private const string DefaultErrorMessage = "The value of {0} cannot be the same as the value of the {1}.";
public string OtherPropertyDisplayName { get; private set; }
public string OtherProperty { get; private set; }
public UnlikeAttribute(string otherProperty)
: base(DefaultErrorMessage)
{
if (string.IsNullOrEmpty(otherProperty))
{
throw new ArgumentNullException("otherProperty");
}
OtherProperty = otherProperty;
}
public override string FormatErrorMessage(string name)
{
return string.Format(ErrorMessageString, name, OtherPropertyDisplayName);
}
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
if (value != null)
{
var otherProperty = validationContext.ObjectInstance.GetType()
.GetProperty(OtherProperty);
var otherPropertyValue = otherProperty
.GetValue(validationContext.ObjectInstance, null);
if (value.Equals(otherPropertyValue))
{
OtherPropertyDisplayName = otherProperty.GetCustomAttribute<DisplayAttribute>().Name;
return new ValidationResult(
FormatErrorMessage(validationContext.DisplayName));
}
}
return ValidationResult.Success;
}
}

Categories