Custom Data Annotation in ASP.NET Core MVC

Custom Data Annotation in ASP.NET Core MVC

In this article, I will discuss How to Create Custom Data Annotations in ASP.NET Core MVC Applications with Real-Time Examples. Please read our previous article discussing Data Annotation Attributes in ASP.NET Core MVC.

Custom Data Annotation in ASP.NET Core MVC

Creating a custom data annotation in ASP.NET Core MVC allows us to encapsulate specific validation logic that built-in data annotation attributes may not provide. Let us see how we can create and use Custom Data Annotation in ASP.NET Core MVC Application step-by-step.

Define the Custom Validation Attribute:
  • Define a new class that derives from the ValidationAttribute class.
  • Override the IsValid method to provide your custom validation logic.

For example, suppose we want to create a custom validation attribute that checks if the provided string has a specific prefix. So, create a class file named StringPrefixAttribute.cs and copy and paste the following code.

using System;
using System.ComponentModel.DataAnnotations;

namespace DataAnnotationsDemo.Models
{
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
    public class StringPrefixAttribute : ValidationAttribute
    {
        private readonly string _prefix;

        public StringPrefixAttribute(string prefix)
        {
            _prefix = prefix ?? throw new ArgumentNullException(nameof(prefix));
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (value is string strValue && !strValue.StartsWith(_prefix))
            {
                return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
            }
            return ValidationResult.Success;
        }

        public override string FormatErrorMessage(string name)
        {
            return $"{name} should start with the prefix '{_prefix}'.";
        }
    }
}

As you can see, the IsValid() method receives two parameters – value and validationContext. The value object parameter supplies the property value on which the StringPrefix attribute is used. Inside, we check whether the value starts with the specified prefix. If not, we return a ValidationResult() object, wrapping an error message. Our custom validation criteria are quite simple in this case, but you can add whatever validation logic you want per your application requirement. If the check passes our validation criteria, we return ValidationResult.Success indicates a successful validation.

Apply the Custom Attribute to the Model Properties:

Now, apply the custom validation attribute to a property in a model or ViewModel. So, create a class file named SampleViewModel.cs and copy and paste the following code.

using System.ComponentModel.DataAnnotations;

namespace DataAnnotationsDemo.Models
{
    public class SampleViewModel
    {
        [Required]
        [StringPrefix("MVC-", ErrorMessage = "The code should start with the 'MVC-' prefix.")]
        public string Code { get; set; }
    }
}

Next, modify the Home Controller as follows:

using DataAnnotationsDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace DataAnnotationsDemo.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Create(SampleViewModel sampleViewModel)
        {
            //Check if the Model State is Valid
            if (ModelState.IsValid)
            {
                //Save the Data into the Database
                //Redirect to a Different View
                return RedirectToAction("Successful");
            }

            //Return to the same View and Display Model Validation error
            return View(sampleViewModel);
        }

        public string Successful()
        {
            return "Employee Addedd Successfully";
        }
    }
}

Next, add Create.cshtml view and copy and paste the following code.

@model DataAnnotationsDemo.Models.SampleViewModel

@{
    ViewData["Title"] = "Create";
}

<div class="row">
    <form asp-controller="Home" asp-action="Create" method="post" class="mt-3">
        <div asp-validation-summary="All" class="text-danger"></div>
        <div class="form-group row">
            <label asp-for="Code" class="col-sm-2 col-form-label"></label>
            <div class="col-sm-10">
                <input asp-for="Code" class="form-control">
                <span asp-validation-for="Code" class="text-danger"></span>
            </div>
        </div>
        <br />

        <div class="form-group row">
            <div class="col-sm-10">
                <button type="submit" class="btn btn-primary">Create</button>
            </div>
        </div>
    </form>
</div>

Now, run the application and see if everything is working as expected. So, creating a custom data annotation in ASP.NET Core MVC allows for flexible and reusable validation logic. The custom validation attribute created above will handle server-side validation. We must extend our custom attribute to implement the IClientModelValidator interface to implement client-side validation and write custom JavaScript to handle the client-side validation logic.

How Client-Side Validation Works in ASP.NET Core MVC?

In ASP.NET Core MVC, client-side validation is primarily enabled using the jQuery Unobtrusive Validation library. This library provides an unobtrusive way to validate HTML form elements. One of this library’s core aspects is using HTML5 data-* attributes to convey validation rules to clients without writing extensive JavaScript. In this context, the attributes that begin with data-val- specify validation rules and error messages.

When we add a data annotation to a model property, the ASP.NET Core MVC tag helpers will automatically generate the necessary data-val-* attributes to represent the validation rules for that property.

For example, given the following model property:
[Required(ErrorMessage = “Code is required.”)]
public string Code { get; set; }

When using ASP.NET Core MVC tag helpers in the Razor view:
<input asp-for=”Code” />
<span asp-validation-for=”Code”></span>

It will render as:
<input class=”form-control” type=”text” data-val=”true” data-val-required=”Code is required.” id=”Code” name=”Code” value=””>
<span class=”text-danger field-validation-valid” data-valmsg-for=”Code” data-valmsg-replace=”true”></span>

Understanding the Attributes:

  • data-val=”true”: Indicates that client-side validation is enabled for this input.
  • data-val-required=”Code is required.”: Specifies that this input has a “required” validation rule and displays the error message if the validation fails.
  • data-valmsg-for=”Code”: Indicates which property this validation message is for.
  • data-valmsg-replace=”true”: Tells the library to replace the content of the validation span with the actual error message if validation fails.
Other data-val-* Attributes:

Depending on the validation attributes we add to our model properties, we might encounter other data-val-* attributes. Some examples include:

  • data-val: Indicates whether client-side validation is enabled for the form element. If this attribute is present, client-side validation is enabled.
  • data-val-required: Used for required field validation. The value of this attribute is the error message to be displayed when the field is left empty.
  • data-val-length: Used for string length validation. The value of this attribute is the error message to be displayed when the string length is not within the specified range.
  • data-val-length-max and data-val-length-min: Specify the maximum and minimum allowed string lengths for string length validation.
  • data-val-number: Used for numeric validation. The value of this attribute is the error message to be displayed when the input is not a valid number.
  • data-val-range: Used for range validation. The value of this attribute is the error message to be displayed when the number is not within the specified range.
  • data-val-range-max and data-val-range-min: Specify the maximum and minimum allowed values for range validation.
  • data-val-regex: For the [RegularExpression] attribute.
  • data-val-email: For email validation.
Custom data-val-* Attributes:

When creating custom validation attributes and implementing the IClientModelValidator interface, we must add our own data-val-* attributes. This is often necessary to pair with custom jQuery validation methods on the client side, ensuring the validation behavior matches between server and client.

JavaScript Dependencies:

For the data-val-* attributes to work, we need to include the following JavaScript libraries:

  • jQuery
  • jQuery Validation
  • jQuery Validation Unobtrusive

These libraries interpret the data-val-* attributes together and perform the client-side validation. The data-val-* attributes allow ASP.NET Core MVC to convey server-side validation rules to the client without the developer having to write extensive client-side validation scripts manually. It ensures consistent validation across servers and clients, which is more streamlined and developer-friendly.

Client-side validation using Custom Data Annotation in ASP.NET Core MVC

Client-side validation improves user experience by providing instant feedback without needing a round trip to the server. To implement client-side validation for custom data annotations in ASP.NET Core MVC, we must write some JavaScript and extend our custom attribute to implement IClientModelValidator.

To add client-side validation to our custom validation attribute, the custom validation attribute class needs to implement the IClientModelValidator interface. This interface provides the AddValidation method, which adds the data-val-* attributes to the generated HTML.

Extending the Custom Data Annotation for Client Side:

Add IClientModelValidator to your custom attribute. Since the StringPrefixAttribute class implements the IClientModelValidator interface, you must write the AddValidation() method. This method sets certain client-side custom data attributes or data-* attributes used by the validation system. Specifically, we set the data-val and data-val-stringprefix attributes. The later data attribute specifies the client-side error message.

using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System;
using System.ComponentModel.DataAnnotations;

namespace DataAnnotationsDemo.Models
{
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
    public class StringPrefixAttribute : ValidationAttribute, IClientModelValidator
    {
        private readonly string _prefix;

        public StringPrefixAttribute(string prefix)
        {
            _prefix = prefix ?? throw new ArgumentNullException(nameof(prefix));
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (value is string strValue && !strValue.StartsWith(_prefix))
            {
                return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
            }
            return ValidationResult.Success;
        }

        public override string FormatErrorMessage(string name)
        {
            return $"{name} should start with the prefix '{_prefix}'.";
        }

        public void AddValidation(ClientModelValidationContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            context.Attributes.Add("data-val", "true");
            context.Attributes.Add("data-val-stringprefix", FormatErrorMessage(context.ModelMetadata.GetDisplayName()));
            context.Attributes.Add("data-val-stringprefix-prefix", _prefix);
        }
    }
}
Understanding the data-val attributes:
  • data-val=”true”: This indicates that validation is enabled for the input element. It essentially activates the unobtrusive validation for this particular input.
  • data-val-stringprefix: It will store the error message to display when validation fails.
  • data-val- stringprefix-prefix: It will store the expected prefix.

Adding the above custom data attributes tells the framework to emit them when the browser renders the view. They don’t perform any validation by themselves. So, you need to hook a jQuery function that does the client-side validation for you.

Create JavaScript for Client-Side Validation:

You can implement the client-side validation logic using jQuery and the jQuery Validation library. Add a new JavaScript file named StringPrefixValidate.js into the wwwroot folder as shown below:

How to Create Custom Data Annotations in ASP.NET Core MVC Applications with Real-Time Examples

Then, write the following code into the StringPrefixValidate.js file. This script adds a new validation method stringprefix, to the jQuery validator. Then, it wires up an adapter to read the data-val-* attributes we set up in our custom validation attribute.

jQuery.validator.addMethod("stringprefix",
    function (value, element, params) {
        if (!value) {
            return true; // Don't validate empty values to allow optional fields
        }
        return value.startsWith(params);
    }, "");

jQuery.validator.unobtrusive.adapters.add("stringprefix", ["prefix"], function (options) {
    options.rules["stringprefix"] = options.params.prefix;
    options.messages["stringprefix"] = options.message;
});

The above code adds a method to the jQuery validation library. It uses the addMethod() method to specify our own validation function. The validation function receives the value entered in the code textbox. It then performs the validation and returns a boolean value. Finally, the add() method hooked this validation function into the library.

You now need to reference the StringPrefixValidate.js in your view. Also, you need to include jQuery, jQuery Validation, and jQuery Unobtrusive Validation scripts. So, modify the Create.cshtml view as shown below.

@model DataAnnotationsDemo.Models.SampleViewModel

@{
    ViewData["Title"] = "Create";
}

<div class="row">
    <form asp-controller="Home" asp-action="Create" method="post" class="mt-3">
        <div asp-validation-summary="All" class="text-danger"></div>
        <div class="form-group row">
            <label asp-for="Code" class="col-sm-2 col-form-label"></label>
            <div class="col-sm-10">
                <input asp-for="Code" class="form-control">
                <span asp-validation-for="Code" class="text-danger"></span>
            </div>
        </div>
        <br />

        <div class="form-group row">
            <div class="col-sm-10">
                <button type="submit" class="btn btn-primary">Create</button>
            </div>
        </div>
    </form>
</div>

<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
<script src="~/js/stringprefixvalidate.js"></script>

With these steps completed, our custom data annotation will trigger client-side validation, providing instant feedback to the user if they don’t follow the rule defined by the StringPrefix attribute. While client-side validation enhances user experience, server-side validation remains critical for security and data integrity. Run the application and check whether client-side validation is working as expected.

Real-Time Examples of Custom Data Annotation in ASP.NET Core MVC

Let’s understand a few real-world scenarios where custom data annotations might come in handy:

Date of Birth Validation:

Suppose you’re building a website where users must register at least 18 years old. You can create a custom validation attribute to ensure this:

Custom Annotation:
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations;

namespace DataAnnotationsDemo.Models
{
    public class MinimumAgeAttribute : ValidationAttribute, IClientModelValidator
    {
        private readonly int _minimumAge;

        public MinimumAgeAttribute(int minimumAge)
        {
            _minimumAge = minimumAge;
        }

        public override bool IsValid(object value)
        {
            if (value is DateTime date)
            {
                return date.AddYears(_minimumAge) <= DateTime.Now;
            }
            return false;
        }

        public override string FormatErrorMessage(string name)
        {
            return $"You must be at least {_minimumAge} years old to register.";
        }

        //Client Side Validation
        public void AddValidation(ClientModelValidationContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            context.Attributes.Add("data-val", "true");
            context.Attributes.Add("data-val-minimumage", FormatErrorMessage(context.ModelMetadata.GetDisplayName()));
            context.Attributes.Add("data-val-minimumage-years", _minimumAge.ToString());
        }
    }
}

Understanding the data-val-* attributes:

  • data-val=”true”: This indicates that validation is enabled for the input element. It essentially activates the unobtrusive validation for this particular input.
  • data-val-minimumage: This stores the error message to display if the validation fails. In our scenario, it would store a message indicating the user must be at least 18 years old (or whatever age is specified).
  • data-val-minimumage-years: This is a custom data-val-* attribute we added. It stores the required minimum age (in our case, 18) used by the client-side validation script to determine whether the user’s age is valid.
Apply the Custom Attribute on Model Properties:
using System.ComponentModel.DataAnnotations;

namespace DataAnnotationsDemo.Models
{
    public class RegistrationViewModel
    {
        [Required]
        [MinimumAge(18)]
        public DateTime DateOfBirth { get; set; }
    }
}

For these data-val-* attributes to be effective on the client side, you’ll need corresponding JavaScript logic:

Create JavaScript for Client-Side Validation:

Add a new JavaScript file named MinimumAgeValidate.js into the wwwroot => js folder. Then, write the following code into the MinimumAgeValidate.js file:

jQuery.validator.addMethod('minimumage', function (value, element, params) {
    if (!value) {
        return true; // Don't validate empty values to allow optional fields
    }

    var minimumAge = parseInt(params);
    var birthDate = new Date(value);
    var currentDate = new Date();
    birthDate.setFullYear(birthDate.getFullYear() + minimumAge);

    return birthDate <= currentDate;
}, '');

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

In the above script:

  • We added a new validation method called minimumage to jQuery Validator.
  • We specify how this validation should work using the data-val-minimumage-years attribute value.
  • We also set the error message to be displayed using the data-val-minimumage attribute value.
Password Strength Checker:

Many applications need users to set strong passwords. You can enforce a rule that the password should contain uppercase, lowercase, numbers, and special characters:

Custom Annotation:

The following custom validation attribute will handle the server-side validation as well as configure client-side validation:

using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations;

namespace DataAnnotationsDemo.Models
{
    public class StrongPasswordAttribute : ValidationAttribute, IClientModelValidator
    {
        public override bool IsValid(object value)
        {
            if (value is string password)
            {
                bool hasUpperCase = password.Any(char.IsUpper);
                bool hasLowerCase = password.Any(char.IsLower);
                bool hasNumbers = password.Any(char.IsDigit);
                bool hasSpecialChar = password.Any(ch => !char.IsLetterOrDigit(ch));

                return hasUpperCase && hasLowerCase && hasNumbers && hasSpecialChar;
            }
            return false;
        }

        public override string FormatErrorMessage(string name)
        {
            return $"The {name} must contain uppercase, lowercase, numbers, and special characters.";
        }

        public void AddValidation(ClientModelValidationContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            context.Attributes.Add("data-val", "true");
            context.Attributes.Add("data-val-strongpassword", FormatErrorMessage(context.ModelMetadata.GetDisplayName()));
        }
    }
}

Understanding the data-val-* attributes:

  • data-val=”true”: Specifies that validation is enabled for the input element. Essentially, it activates the unobtrusive validation for this particular input.
  • data-val-strongpassword: This holds the error message to display if the validation fails. Our scenario could be a message stating that the password must contain uppercase letters, lowercase letters, numbers, and special characters.
Apply the Custom Attribute on Model Properties:
using System.ComponentModel.DataAnnotations;
namespace DataAnnotationsDemo.Models
{
    public class ChangePasswordViewModel
    {
        [Required]
        [StrongPassword]
        public string NewPassword { get; set; }
    }
}
Create JavaScript for Client-Side Validation:

Add a new JavaScript file named StrongPasswordValidate.js into the wwwroot => js folder. Then, write the following code into the StrongPasswordValidate.js file:

jQuery.validator.addMethod('strongpassword', function (value, element) {
    if (!value) {
        return true; // Don't validate empty values
    }

    var hasUpperCase = /[A-Z]/.test(value);
    var hasLowerCase = /[a-z]/.test(value);
    var hasNumbers = /\d/.test(value);
    var hasSpecialChar = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+/.test(value);

    return hasUpperCase && hasLowerCase && hasNumbers && hasSpecialChar;
}, '');

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

In this script:

  • We added a new validation method named strongpassword to the jQuery Validator.
  • This method checks the password against the defined criteria.
  • We also tell jQuery Validator to use the data-val-strongpassword attribute to fetch the error message when the password doesn’t meet the required criteria.
Credit Card Expiration Date:

For an e-commerce website, you might want to validate that the provided credit card’s expiration date is in the future.

Custom Annotation:

The following custom validation attribute will handle the server-side validation and also set up the client-side validation attributes:

using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations;
namespace DataAnnotationsDemo.Models
{
    public class FutureDateAttribute : ValidationAttribute, IClientModelValidator
    {
        public override bool IsValid(object value)
        {
            if (value is DateTime date)
            {
                return date > DateTime.Now;
            }
            return false;
        }

        public override string FormatErrorMessage(string name)
        {
            return $"{name} must be in the future.";
        }

        public void AddValidation(ClientModelValidationContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            context.Attributes.Add("data-val", "true");
            context.Attributes.Add("data-val-futuredate", FormatErrorMessage(context.ModelMetadata.GetDisplayName()));
        }
    }
}

Understanding the data-val-* attributes:

  • data-val=”true”: Indicates that validation is enabled for the input element. It triggers the unobtrusive validation for this specific input.
  • data-val-futuredate: This contains the error message to display if the validation fails. In our case, it might show a message suggesting that the card’s expiration date is past and thus invalid.
Apply the Custom Attribute on Model Properties:
using System.ComponentModel.DataAnnotations;
namespace DataAnnotationsDemo.Models
{
    public class PaymentViewModel
    {
        [Required]
        [Display(Name = "Expiration Date")]
        [FutureDate(ErrorMessage = "The credit card has expired.")]
        public DateTime ExpiryDate { get; set; }
    }
}

For the data-val-* attributes to facilitate client-side validation, corresponding JavaScript logic is necessary:

Create JavaScript for Client-Side Validation:

Now, create the client-side validation logic. Add a new JavaScript file named FutureDateValidate.js into the wwwroot => js folder. Then, write the following code into the FutureDateValidate.js file:

jQuery.validator.addMethod('futuredate', function (value, element) {
    if (!value) {
        return true; // Don't validate empty values
    }

    var expiryDate = new Date(value);
    var currentDate = new Date();
    return expiryDate >= currentDate;
}, '');

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

In this script:

  • A new validation method called futuredate is added to jQuery Validator.
  • This method checks if the date is in the future.
  • It also uses the data-val-futuredate attribute to extract the error message if the date isn’t in the future.
Custom Email Domain:

If you’re creating an internal application and only want company emails to be able to register, you can validate the email domain:

Custom Annotation:
using System.ComponentModel.DataAnnotations;
namespace DataAnnotationsDemo.Models
{
    public class CompanyEmailAttribute : ValidationAttribute
    {
        private readonly string _domain;

        public CompanyEmailAttribute(string domain)
        {
            _domain = domain;
        }

        public override bool IsValid(object value)
        {
            if (value is string email)
            {
                return email.EndsWith("@" + _domain, StringComparison.OrdinalIgnoreCase);
            }
            return false;
        }

        public override string FormatErrorMessage(string name)
        {
            return $"Only {_domain} domain is allowed for {name}.";
        }
    }
}
Apply the Custom Attribute on Model Properties:
using System.ComponentModel.DataAnnotations;

namespace DataAnnotationsDemo.Models
{
    public class EmployeeRegistrationViewModel
    {
        [Required]
        [CompanyEmail("dotnettutorials.net", ErrorMessage = "Please use your company email.")]
        public string Email { get; set; }
    }
}
File Size and Type Checker for Uploads:

When users upload files, say profile pictures, you might want to enforce file size and type restrictions.

Custom Annotation:
using System.ComponentModel.DataAnnotations;
namespace DataAnnotationsDemo.Models
{
    public class FileSizeAttribute : ValidationAttribute
    {
        private readonly int _maxSizeInMb;

        public FileSizeAttribute(int maxSizeInMb)
        {
            _maxSizeInMb = maxSizeInMb;
        }

        public override bool IsValid(object value)
        {
            if (value is IFormFile file)
            {
                return file.Length <= _maxSizeInMb * 1024 * 1024;
            }
            return true;
        }

        public override string FormatErrorMessage(string name)
        {
            return $"The file size for {name} should not exceed {_maxSizeInMb}MB.";
        }
    }
}
Apply the Custom Attribute on Model Properties:
using System.ComponentModel.DataAnnotations;

namespace DataAnnotationsDemo.Models
{
    public class UserProfileViewModel
    {
        [Required]
        [FileSize(3, ErrorMessage = "Please upload an image smaller than 3MB.")]
        public IFormFile ProfilePicture { get; set; }
    }
}
Custom Postal Code Validation:

Suppose you’re operating within specific regions and want to ensure that users input only postal codes belonging to those regions.

Custom Annotation:
using System.ComponentModel.DataAnnotations;
namespace DataAnnotationsDemo.Models
{
    public class PostalCodeAttribute : ValidationAttribute
    {
        private readonly string[] _allowedPostalCodes;

        public PostalCodeAttribute(params string[] allowedPostalCodes)
        {
            _allowedPostalCodes = allowedPostalCodes;
        }

        public override bool IsValid(object value)
        {
            if (value is string postalCode)
            {
                return _allowedPostalCodes.Contains(postalCode);
            }
            return false;
        }

        public override string FormatErrorMessage(string name)
        {
            return $"{name} should belong to one of the following postal codes: {string.Join(", ", _allowedPostalCodes)}.";
        }
    }
}
Apply the Custom Attribute on Model Properties:
using System.ComponentModel.DataAnnotations;

namespace DataAnnotationsDemo.Models
{
    public class ShippingViewModel
    {
        [Required]
        [PostalCode("12345", "67890", ErrorMessage = "We don't ship to this postal code.")]
        public string PostalCode { get; set; }
    }
}

These examples demonstrate how custom data annotations can address the specific needs of real-world applications, providing consistent and centralized validation logic across the application. The primary advantage of this approach is the encapsulation of validation logic, making it reusable and maintainable.

In the next article, I will discuss Remote Validation in ASP.NET Core MVC Application. In this article, I try to explain Custom Data Annotation in ASP.NET Core MVC Application with examples. I hope you enjoy this Custom Data Annotation in ASP.NET Core MVC article.

Leave a Reply

Your email address will not be published. Required fields are marked *