Click here to Skip to main content
14,492,702 members

How to Create Custom Validation Attribute in C#

Rate this:
5.00 (5 votes)
Please Sign up or sign in to vote.
5.00 (5 votes)
21 Jan 2020CPOL
How to create a simple validation attribute using C#

Introduction

When creating web applications such as REST, we must handle the situation in which we receive incorrect data. Often, ready-made attributes are not enough for us to check the data correctly, so in this tip, I will try to explain how we can write our own validator.

Using the Code

The first step will be to create a new application.

Image 1

The next step will be installing the required nuget packs. You can do it directly from the console or using the menu manager.

Install-Package System.Linq.Dynamic -Version 1.0.7

This post will create a custom validator using the conditional required example.

Now let's create a class that inherits from "ValidationAttribute". Something like that:

using System;
using System.ComponentModel.DataAnnotations;

namespace HRP.Services.Attributes
{
    public class RequiredIfAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid
                  (object value, ValidationContext validationContext)
        {

        }
    }
}

This class should override the inherited IsValid method. This will allow us to connect to the place where the data is validated.

The whole will be based on passing to the attribute an expression in the form of a string which will then be reparsed and applied to the data. To do this, we need to add a constructor that takes a string value and then assigns it to a variable.

private readonly string _condition;

public RequiredIfAttribute(string condition)
{
    _condition = condition;
}

The next step will be to create a method that accepts this expression in the form of text and parses it to a specific value.

private static Delegate CreateExpression(Type objectType, string expression)
        {
            var lambdaExpression =
                DynamicExpression.ParseLambda(
                    objectType, typeof(bool), expression);
            var func = lambdaExpression.Compile();
            return func;
        }

Now that we have such a method prepared, we should use it in the method for checking data correctness.

protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var conditionFunction = CreateExpression(
                validationContext.ObjectType, _condition);
            var conditionMet = (bool) conditionFunction.DynamicInvoke(
                validationContext.ObjectInstance);
            if (!conditionMet) return null;

            if (value != null && int.TryParse(value.ToString(), out var parsedValue))
            {
                return parsedValue == 0
                    ? new ValidationResult($"Field {validationContext.MemberName} is required")
                    : null;
            }

            return new ValidationResult($"Field {validationContext.MemberName} is required");
        }

We already have everything ready, now it is enough to apply this validator in the model, e.g., in such a way.

public class GetHotelReviewRequest
   {
       [RequiredIf("AllRecords==false")]
       [Range(0, 10, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
       public int ItemsPerPage { get; set; }

       [RequiredIf("AllRecords==false")]
       [Range(0, int.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
       public int Page { get; set; }

       [Required] public bool AllRecords { get; set; }
   }

I personally used this attribute for paging. I have one "AllRecords" value that is required. And when the application receives false value, all fields in the model that have the attribute [RequiredIf ("AllRecords == false")] will also have to be filled.

Summary

To sum up, the attribute created by us only checks if a given field has any value and in the case of "int", it is different from zero or default values. The attribute can, of course, be expanded to check different types of values, however in this tip, the main goal was to show the use of DynamicExpression to create your own attribute.

History

  • 21st January, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

kr.is
Poland Poland
No Biography provided

Comments and Discussions

 
Questionnot working for .net Core? Pin
Member 1469962622-Jan-20 3:21
MemberMember 1469962622-Jan-20 3:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Tip/Trick
Posted 21 Jan 2020

Stats

4.1K views
80 downloads
9 bookmarked