0

I am implementing a validation for a group of fields, in my case one of these three fields is mandatory.

I am implementing a custom data annotation.

My metadata is the following. In my metadata I have put this data annotation which is the one I want to customize.

public class document
{
    [RequireAtLeastOneOfGroupAttribute("group1")]
    public long? idPersons { get; set; }

    [RequireAtLeastOneOfGroupAttribute("group1")]
    public int? idComp { get; set; }

    [RequireAtLeastOneOfGroupAttribute("group1")]
    public byte? idRegister { get; set; }

    public bool other1{ get; set; }
    .
    public string otherx{ get; set; }      
}

On the other hand I have created a class called RequireAtLeastOneOfGroupAttribute with the following code.

[AttributeUsage(AttributeTargets.Property)]
public class RequireAtLeastOneOfGroupAttribute : ValidationAttribute, IClientValidatable
{
    public string GroupName { get; private set; }
    public RequireAtLeastOneOfGroupAttribute(string groupName)
    {
        ErrorMessage = string.Format("You must select at least one value from group \"{0}\"", groupName);
        GroupName = groupName;            
    }           

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        foreach (var property in GetGroupProperties(validationContext.ObjectType))
        {
            var propertyValue = (bool)property.GetValue(validationContext.ObjectInstance, null);
            if (propertyValue)
            {
                // at least one property is true in this group => the model is valid
                return null;
            }
        }
        return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
    }

    private IEnumerable<PropertyInfo> GetGroupProperties(Type type)
    {
        IEnumerable<PropertyInfo> query1 = type.GetProperties().Where(a => a.PropertyType == typeof(int?) || a.PropertyType == typeof(long?) || a.PropertyType == typeof(byte?));

        var metadataType = type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).OfType<MetadataTypeAttribute>().FirstOrDefault();

        var metaData = (metadataType != null)
        ? ModelMetadataProviders.Current.GetMetadataForType(null, metadataType.MetadataClassType)
        : ModelMetadataProviders.Current.GetMetadataForType(null, type);


        var propertMetaData = metaData.Properties
                                        .Where(e =>
                                        {
                                            var attribute = metaData.ModelType.GetProperty(e.PropertyName)
                                                .GetCustomAttributes(typeof(RequireAtLeastOneOfGroupAttribute), false)
                                                .FirstOrDefault() as RequireAtLeastOneOfGroupAttribute;
                                            return attribute == null || attribute.GroupName == GroupName;
                                        })
                                        .ToList();

        RequireAtLeastOneOfGroupAttribute MyAttribute =
          (RequireAtLeastOneOfGroupAttribute)Attribute.GetCustomAttribute(type, typeof(RequireAtLeastOneOfGroupAttribute));

        if (MyAttribute == null)
        {
            Console.WriteLine("The attribute was not found.");
        }
        else
        {
            // Get the Name value.
            Console.WriteLine("The Name Attribute is: {0}.", MyAttribute.GroupName);                
        }

        return consulta1;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {            
        var groupProperties = GetGroupProperties(metadata.ContainerType).Select(p => p.Name);           
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = this.ErrorMessage
        };
        rule.ValidationType = string.Format("group", GroupName.ToLower());
        rule.ValidationParameters["propertynames"] = string.Join(",", groupProperties);
        yield return rule;
    }
}

The code works correctly when the form is called the GetClientValidationRules function is called.

My problem is in the MyAttribute query because it always returns null. The GetCustomAttribute function never gets results. I have tried different variants without success, it is as if that data annotation did not exist.

I just implemented the query with the view part and javascript, which is the following.

jQuery.validator.unobtrusive.adapters.add(
'group',
['propertynames'],
function (options) {
    options.rules['group'] = options.params;
    options.messages['group'] = options.message;
});

jQuery.validator.addMethod('group', function (value, element, params) {
var properties = params.propertynames.split(',');
var isValid = false;
for (var i = 0; i < properties.length; i++) {
    var property = properties[i];
    if ($('#' + property).is(':checked')) {
        isValid = true;
        break;
    }
}
return isValid; }, '');

and the view

@Html.TextBoxFor(x=> x.document.idPersons, new { @class= "group1" })
@Html.TextBoxFor(x=> x.document.idComp, new { @class= "group1" })
@Html.TextBoxFor(x=> x.document.idRegister, new { @class= "group1" })
@Html.TextBoxFor(x=> x.document.other1)
@Html.TextBoxFor(x=> x.document.otherx)
CodeNotFound
  • 22,153
  • 10
  • 68
  • 69
Geo
  • 309
  • 4
  • 22
  • 1
    You can take a look at my answer here. How did the same thing without writing any javascript by mine => https://stackoverflow.com/a/38920584/797882 – CodeNotFound May 03 '18 at 09:32
  • As a side note, you script will not work - your `if ($('#' + property).is(':checked')) {` only applies to checkboxes –  May 03 '18 at 09:37
  • You appear to have copied everything from Darin Dimitrov's [working answer](https://stackoverflow.com/questions/7247748/mvc3-validation-require-one-from-group/38920584#38920584) except the `GetGroupProperties()` method. Why? –  May 03 '18 at 09:46
  • if it is copied from that example, in my case I will not apply it only to booleans. Thanks for the help. – Geo May 03 '18 at 10:18
  • Thanks for the help CodeNotFound your example has worked perfectly for me. Only in the `RequireFromGroupAttribute` class that use the `using System.Reflection;` – Geo May 04 '18 at 08:34

0 Answers0