Bootable Template 3-column Theme + Layout for Bootstrap 3.
Using resource file for dataannotations display attribute with multi language support in asp.net mvc.
We use the attributes defined in System.ComponentModel.DataAnnotations namespace to supply meta data for model classes. This Meta data defined using these attributes will be used by Entity Framework for model generation and Asp.Net MVC framework for validations and scaffolding UI components, etc. One such attribute is DisplayAttribute which is used to define the display title for the field. The value defined in this attribute will be used by the html helper (@Html.DisplayNameFor(model => model.FirstName)) to display the title for the field.
At times, it would be helpful if we can provide resource files to define the display title instead of hard coding inside the [Display] attribute. By using resource file, it will be easier to change the display name instead changing the C# class file. It also helps in localization of display attributes. This simple article will help us to manage the display titles using resource files with multi language support in an Asp.Net MVC application. The Display attribute already includes supports reading from resource file through a parameter called ResourceType.
For demonstration, we will use a simple Employee-Department model with Entity Framework Code First and Asp.Net MVC 5.0 solution using Visual Studio 2015. The project requires all the Nuget packages required for Asp.Net MVC 5.0 and the latest Entity Framework package included in the solution.
Creating Resource File and Configuring Model Class
To add a resource file into your solution, right click project in solution explorer and click Add > New Item.. In the Add New Item dialogue, select “Resources File” under General category. Rename the file name to EmployeeResx.resx and Click Add. I have added the file under Models/RESX folder in my solution.
Now, let’s add display attribute to the properties of Employee object and configure it to look for the display title in EmployeeResx.resx file. Code below.
public class Employee { //Table properties [Key] public int EmployeeId { get; set; } public int DepartmentId { get; set; } [MaxLength(50)] [Display(Name = "FirstName", ResourceType = typeof(EmployeeResx))] public string FirstName { get; set; } [Display(Name = "LastName", ResourceType = typeof(EmployeeResx))] [MaxLength(50)] public string LastName { get; set; } [Display(Name = "Address1", ResourceType = typeof(EmployeeResx))] [MaxLength(50)] public string Address1 { get; set; } [Display(Name = "Address2", ResourceType = typeof(EmployeeResx))] [MaxLength(50)] public string Address2 { get; set; } [Display(Name = "City", ResourceType = typeof(EmployeeResx))] [MaxLength(50)] public string City { get; set; } [Display(Name = "State", ResourceType = typeof(EmployeeResx))] [MaxLength(50)] public string State { get; set; } [Display(Name = "Country", ResourceType = typeof(EmployeeResx))] [MaxLength(50)] public string Country { get; set; } [Display(Name = "PostalCode", ResourceType = typeof(EmployeeResx))] [MaxLength(10)] public string PostalCode { get; set; } [Display(Name = "Email", ResourceType = typeof(EmployeeResx))] [MaxLength(100)] public string Email { get; set; } [Display(Name = "DOB", ResourceType = typeof(EmployeeResx))] public DateTime? DOB { get; set; } [Display(Name = "Gender", ResourceType = typeof(EmployeeResx))] [MaxLength(10)] public string Gender { get; set; } [Display(Name = "IsPermanent", ResourceType = typeof(EmployeeResx))] public bool IsPermanent { get; set; } [Display(Name = "Salary", ResourceType = typeof(EmployeeResx))] public decimal Salary { get; set; } public virtual Department Department { get; set; } }
Next, we need to add the key-value pair for all the Employee properties in the added resource file like below. The Name should match the value defined for Name parameter in Display attribute.
When executed, you can see the display title coming from the resource file like below.
Note : You may get an error “Cannot retrieve property 'Name' because localization failed. Type 'Xxxx.EmployeeResx' is not public or does not contain a public static string property with the name 'FirstName'.“. This is because, by default the resource file properties have access modifier set to internal. Change it to Public in the Access Modifier dropdown in the resource file toolbar. Refer below image.
Now, let’s add another resource file for adding support for another language, for example, Tamil. Add a new resource file and name it as EmployeeResx.ta.resx. The file name must include language code ta for Tamil as part of the name (EmployeeResx .ta. resx). Doing this will make the resource file to get picked automatically when the current UI thread culture is set to culture code ta-IN. Refer here to know the list of language-country code. The Tamil resource file will look like below.
When executed, you can see the title changed to Tamil when the current UI thread culture is set to Tamil.
I have set the current UI thread’s culture from Global.asax file by accepting the Request object’s language header set by the client browser.
protected void Application_AcquireRequestState(object sender, EventArgs e) { if (Request.UserLanguages != null) { string culture = Request.UserLanguages[0]; Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture); Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture); } }
To make the browser add the language header, in Firefox, go to Tools>Options>Content. Click Choose.. button under Languages. Add the preferred language (Tamil in this article) from the dropdown and use the Move Up button to make it as first preference.
Download the source and see it in action.

Related Read
Latest tutorials.
- Hosting or Deploying Asp.Net MVC site in Godaddy Shared Hosting
- How to Stream Music from PC/Desktop/NAS HardDrive to Amazon Alexa
Top Stories
- 5 Tips on How to Use Twitter Effectively for Your IT Career
- How to Choose a Real World Network Security
- Is It Easy to Learn Java if You Already Know JavaScript?
- New to ASP.NET? Here’s the Simplest Code You Can Execute
- Top 5 programming languages commonly used by online gaming developers
Sponsored Links
Latest quickstart guides.
- What is Web Accessibility Guidelines & Section 508 Compliance? Build Screen Reader Friendly WebSites
- What is Dapper? How to Use Dapper in Asp.Net MVC?
- What is REST Services? How to Create REST Services in Asp.Net?
- What is OWASP? What are OWASP Top 10 Security Risks?
- Using Gulp (gulpfile.js) in Visual Studio 2017
- What is Gulp? How to Use Gulp?
- What is Entity Framework Code First Migration? A Beginners Guide
- What is Dependency Injection? How to use this Pattern in Asp.Net MVC?
- Learn Asynchronous Programming (async, await) in C# in 10 Minutes
- What is .Net Standard? How it is different from .Net Core and Portable Class Library?
Browse by category
- Stack Overflow Public questions & answers
- Stack Overflow for Teams Where developers & technologists share private knowledge with coworkers
- Talent Build your employer brand
- Advertising Reach developers & technologists worldwide
- About the company
Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
DataAnnotation with custom ResourceProvider
I have created a custom ResourceProvider to pull localization information from a database. I now want to use DataAnnotation to add validation to the model.
DataAnnotation has ErrorMessageResourceType and ErrorMessageResourceName properties but ErrorMessageResourceType only accepts System.Type (i.e. a compiled resource file)
Is there any way to get DataAnnotation to use the custom ResourceProvider?
- asp.net-mvc
- localization

3 Answers 3
I realize this is an old question, but wanted to add a bit. I found myself in the same situation and there doesn't appear to be any documentation/blogumentation on this topic. Nevertheless, I figured out a way to use a custom resource provider, with one caveat. The caveat is that I'm in an MVC application so I still have HttpContext.GetLocalResourceObject() available. This is the method that asp.net uses to localize items. The absence of the resource object doesn't stop you from writing our own solution, even if its a direct query of the DB tables. Nevertheless, I thought it was worth pointing out.
While I'm not terribly happy with the following solution, it seems to work. For each validation attribute I want to use I inherit from said attribute and overload the IsValid(). The decoration looks like this:
The new attribute looks like this:
- You need to decorate your code with the derived attribute, not the standard one
- MVC_HtmlHelpers.Localize is just a simple wrapper for my custom resource provider
The (semi-stolen) helper code looks like this ....
I have used fluent validation to achieve this. It saves me lots of time. This is what my Globalized validator looks like. It does mean that you don't use data anotations, but sometimes data anotations get a bit big and messy.
Here is an example:
(Errors.Required, Labels.Email and Errors.AlreadyRegistered are in my blobal resources folder.)
Like I said, it is a move away form data annotations, only because I already have too many annotations on my methods already!
I'll add my findings since I had to fight with this. Maybe it will help someone.
When you derive from RequiredAttribute, it seems to break client side validation. So to fix this I implemented IClientValidatable and implemented the GetClientValidationRules method. Resources.GetResources is static helper method I have that wraps around HttpContext.GetGlobalResourceObject.
The custom required attribute:
And my Resources helper if anyone is interested:
Your Answer
Sign up or log in, post as a guest.
Required, but never shown
By clicking “Post Your Answer”, you agree to our terms of service , privacy policy and cookie policy
Not the answer you're looking for? Browse other questions tagged c# asp.net-mvc localization resources or ask your own question .
- The Overflow Blog
- Five Stack Exchange sites turned ten years old this quarter!
- “Move fast and break things” doesn’t apply to other people’s savings (Ep. 544)
- Featured on Meta
- We've added a "Necessary cookies only" option to the cookie consent popup
- Launching the CI/CD and R Collectives and community editing features for...
- The [amazon] tag is being burninated
- Temporary policy: ChatGPT is banned
- Staging Ground Beta 1 Recap, and Reviewers needed for Beta 2
Hot Network Questions
- How does an ideal prior distribution needs a probability mass on zero to reduce variance, and have fat tails to reduce bias?
- Gas furnace takes like 2-5 minutes from ignition to blower start. flame also burning yellow/orange
- An Effective Splash Cymbal
- Chimeric Animals from Icewind Dale
- Can there be a repulsion between an electron beam and a proton beam depending on the beam's velocities?
- In England, why are some high schools called hospitals?
- are there any legal systems in the world where the judiciary and the legislature are the same?
- How to find most cited researchers in a specific field?
- Why are aluminum frames painted at all?
- 21 hr layover in ORY airport. Bad idea?
- Would Fey Ancestry affect Cutting Words?
- Animation which involves a spherical floating robot and a little girl
- Do Catholic anathemas apply to people who lived before it was codified?
- Are you saving 'against' an effect if that effect applies when you successfully save?
- What laws would Jesus be breaking if he were to turn water into wine today?
- Are there any medieval manuals relating to castle building?
- What would be the advantage of launching an UN-led inquiry over the Nord Stream sabotage?
- How can I map a single d20 to a number of hits? (use case: Animate Objects)
- Working directly on DateObjects
- Should the sticker on top of a HDD be peeled?
- Checksum of secret data
- Why is the work done by the tension in a pendulum string 0?
- Mando's Mandatory Meandering?
- Properties of categorical zeta function
Your privacy
By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy .
- Forums home
- Browse forums users
- Remove From My Forums
Answered by:
[required()] data annotation and resource files..
I am having an issue where I want to use a resource messag for my Required() error message and it does not function as apparently documented. I have a similar CustomValidator that works correctly.
Is this a known issue with the latest release, and if not what's the best way to trouble shoot this?
This code functions as expected:
If I switch it to below, it works.
When trying to validate the code that's not working, the data form no longer shows the field as required, nor does it trigger any validation.
Just to confirm, are you using the RIA Services RC build with Visual Studio 2010? If so, create a new project and select the 'Silverlight Business Application' template under the Silverlight category. This has a RegistrationData type that uses the RequiredAttribute like I described above. Can you start the app and see what the Registration window looks like (click Login then click Register on the dialog)? This should behave like you've described. If it does, is there a difference between the Toolkit (DataForm) you have installed and the one included in the template?
All replies
Could you include a little more information about the error you're seeing? The RequiredAttribute definitely works as you're expecting in some scenarios. For instance, it's used exactly as you have it above in the Business Application Template. Do you know what's different about where or how you're using it? Thanks.
There is no error at all.
What I have a data form, bound to the entity with autogenerate columns on (for testing purposes).
If I use the syntax:
[Required(ErrorMessage="Last name is required.")] then the last name field displays with a Bold Label and if I edit it to blank and tab off it, the validation fires.
If I used the syntax: (I'm paraphrasing a little from memory so there might be a typo)
[Required(ErrorMessageResourceName="LastNameIsRequired",ErrorMessageResourceType=typeof(myresourcefile))]
The label no longer appears bolded and when I clear the last name the validation doesn't fire.
As mentioned I feel pretty confident that my resource files are set up properly because I am using the same Syntax for a Custom Validator on the same entity from the same resource file and that functions as expected.
What I meant is that the "Success" case that is exactly opposite what you're seeing is part of one of the templates that is shipped as part of RIA Services. This line is in RegistrationData.cs in the Business Application Template and works exactly how you'd expect it to.
I'm just curious what is different between your case and the template that might be causing the behavior you're seeing.
Mm, good question. I will have to investigate further by downloading that solution again and comparing I guess. Will do that tomorrow.
OK, I downloaded the HR_App code where you mentioned its implemented and here's my experience:
1. While the fields are annotated as required using the syntax I want to use, the labels are not Bold (like they are when just the [Required()] version is using.
2. I can type in the fields and then blank it out and I never get any feedback that it is required.
3. If I fill in only the registration fields for Password and Confirm Password, I get a Submit Operations error then all the fields turn red that were required.
4. So, unless I'm missing something. The required syntax using the Resource Syntax is either broken in RIA or in the data form.
Thanks for that hint, you're correct. My project is referencing version: 4.0.31118.0255, the default Business Template provides me a reference to: 4.0.40217.2256.
Unless I'm mistaken the only toolkit available for download right now is from 11/09 which is: 4.0.31118.0255
Seems the one in th template is a newer build and yes, that one does function.
Is it as simple as replacing my external lib file with the one Visual Studio gave me or should I wait for the next toolkit release?
I updated my reference to this version and the problem is resolved. Thank you.

- An Interview Question
- TECHNOLOGIES
- INTERVIEW PREP
- Intermediate
- Jignesh Trivedi (3)
- Juan Francisco Morales Larios (2)
- Debendra Dash (1)
- Pankaj Kumar Choudhary (1)
- Ankur Mistry (1)
- Sandeep Singh Shekhawat (1)
- Abhay Shanker (1)
- Understand Validation In Blazor Apps 2/17/2020 12:25:46 PM. In this article, you will learn about how to do validation in Blazor server and webassembly application
- Model Class Validation Testing Using Nunit 2/15/2017 11:35:42 AM. In this article, we are going to learn about validating a Model class property that has data annotation, using Nunit.
- Validation Forms In DataAnnotations 12/14/2016 3:22:10 AM. In this article, you will learn about validation forms in DataAnnotations.
- DataAnnotations In Depth 11/28/2016 1:12:39 PM. In this article you will learn about DataAnnotations in depth.
- Move Domain Classes Configuration To Separate Classes 5/19/2016 11:44:34 AM. In this article you will learn how to move Domain Classes Configuration to separate classes.
- Validation Using Data Annotation In ASP.NET MVC 1/9/2016 12:48:15 PM. In this article we will learn to implement simple validation using Data Annotation Attributes.
- Relationship in Entity Framework Using Code First Approach With Fluent API 4/13/2015 2:05:22 AM. In this article you will learn about relationships in Entity Framework using the Code First Approach with Fluent API.
- Validate Field in MVC5 Using Resource File 6/12/2014 12:37:53 PM. This article provides an overview of validation in ASP.NET MVC 5 using a Resource file.
- Creating Custom Validation Attribute For Data Annotation 6/27/2012 8:52:25 PM. In .net 4.0, namespace System.ComponentModel.DataAnnotations allow you to create new attribute and with the help of this you can validate the data as per your requirement.
- Validation Using Data Annotation to Custom Model or Class 6/27/2012 8:25:17 PM. In this tutorial, we learn how to use the Data Annotation validators to perform validation in a .NET application.
Shared RESX file for data annotation localization in ASP.NET Core 2.0
Posted on: 20-03-2018
This is a continuation to my ASP.NET Core Localization Deep Dive .
Something which was asked here today by Dwayne Hawkins was:
Why can't it just use the SharedResources file, which I made for my views. I don't want 5000 seperate resx files for all my viewmodels ...
A very valid concern. If we have a lot of view models, we would not want to copy paste error message translations like "{0} is required" to hundreds of RESX files. Updates would be a nightmare. So I found a solution for the problem. I am not sure if it is the best solution, but it does work.
Configuring the Data Annotation Localizer Provider
First I noticed there is an overload on AddDataAnnotationsLocalization which takes an Action which can be used to configure options. The options class is pretty simple:
It has only one property. But that is the delegate used to create the localizers! By default, it uses the type of the model to create a localizer with the localizer factory.
Here is what I did:
SharedResource here is an empty class. I have a corresponding SharedResource.en-US.resx and SharedResource.fi-FI.resx file in my Resources folder. This means any annotation localizations will now come from those files!
The Finnish RESX file contains values like this:
- Name : Nimi
- {0} is required : {0} on pakollinen
Then if we have a model, we can do annotations like this:
And we get localized error messages :)
Shorter article this time, hope this is useful!
You might also like these related articles
- ASP.NET Core Localization Deep Dive
- Data Center
- Applications
- Open Source

You’ve completed a hefty round of raw data collection, and now you want to feed that information into artificial intelligence (AI) machines, so they can perform human-like actions. The problem: These machines can only act according to the parameters you establish for the data set. Data annotation is the primary solution that bridges the gap between sample data and AI/machine learning.
Data annotation is a process where a human data annotator goes into a raw data set and adds categories, labels, and other contextual elements, so machines can read and act upon the information.
The annotated raw data used in AI and machine learning often consists of numerical data and alphabetical text, but data annotation can also be applied to images and audiovisual elements.
See more below about how data annotation is used in applications and some of the current and future benefits the practice offers.
Data Annotation Overview
Types of data annotation, data annotation features, benefits of data annotation, data annotation use cases, data annotation market.
Dive deeper into artificial intelligence: Top Performing Artificial Intelligence Companies of 2021
Depending on what you want your AI to accomplish and what data sources it will need, different types of annotation should be used. The most common types of annotation are text, image, audio, and video.
Text annotation
Text annotation focuses on adding labels and instructions to raw text, which enables AI to recognize and understand how typical human sentences and other textual data are structured for meaning.
There are three primary categories of text annotation that elucidate different meanings within data sets:
- Sentiment: In sentiment annotation, a human annotator collects training text for AI, but first, they make note of the emotional intonation and other subjective implications behind keywords and phrases. Sentiment annotation helps AI to understand the underlying meaning of texts beyond dictionary definitions. This type of annotation is particularly useful for AI-powered moderation on social media platforms.
- Intent: Intent annotation resembles sentiment annotation, but in this category, the annotator focuses on labeling the human intent, or the user’s end goal, behind different statements. Intent annotation provides insight in the realm of customer service, where AI-powered chatbots need to understand what specific results or information they should deliver to a human user.
- Semantic: Buyer-seller relationships drive semantic annotation, which works to provide clearer labels on product listings, so AI can suggest, or produce in search results, exactly what customers are seeking.
Image annotation
At its most basic level, image annotation focuses on labeling images with metadata, keywords, and other descriptors that explain the image in relation to other image descriptors. Image annotation makes images accessible to users who use screen readers, and it also helps websites like stock image aggregators identify and deliver photos for user queries.
Image annotation has expanded AI capabilities over the years, now adding contextual annotations to detailed images of streets and human bodies, which provide training data for self-driving vehicles and medical diagnostic tools.
Audio annotation
Many mobile and Internet of Things (IoT) devices rely on speech recognition and other audio comprehension features, but they only learn audial meanings through the practice of audio annotation. Audio annotators handle raw data in the form of speech and other sound effects, and then they label and categorize audio clips based on qualities like pronunciation, intonation, dialect, and volume, among others. IoT devices like home assistants rely on the speech and audio recognition that comes from audio annotation.
More on this topic: The Conversational AI Revolution: The Threat and the Opportunity
Video annotation
Video annotation combines several features of image and audio annotation, helping AI to assess the meaning of sound and visual elements in a video clip. Video annotation has become particularly important in the development of technologies like self-driving cars and in-home IoT devices .
In every type of data annotation, a few key tools help make annotation possible:
- Ontologies: Think of ontologies as the blueprints to provide accurate and helpful annotation frameworks. Ontologies include information like annotation types, labeling guidelines, and class and attribute standards.
- Sample sets of smart data: You can’t practice data annotation without the right sample data. Raw data comes in endless forms, so it’s important to pick “smart” raw data or data that is relevant to the training of your specific AI tools. This data is usually collected from historic human interaction data the company has on file, but sometimes, open source data will meet the needs of the data annotation project.
- Data set management and storage tools: Annotating data for AI and machine learning projects requires a large amount of raw data. To keep both raw and annotated data organized and easily accessible, you need to manage and store it in a file system or software that can handle the bandwidth.
Data annotation impacts a wide variety of AI and machine learning technologies and brings many benefits to companies and their customers:
- Chatbots and voice assistants have been trained to have more human-like conversations with customers.
- Higher-quality results are returned for search queries.
- In-home IoT devices can detect everything from a human voice to a sudden movement in the home, which improves accessibility and home security.
- Online videos, images, and articles have become increasingly accessible for users who have vision or hearing impairments. Speech recognition technology has increased the range of accessibility on mobile and desktop devices as well.
- Facial and bodily recognition tools can be used for anything from increased biosecurity to AI-powered medical diagnoses.
- New technologies like self-driving cars can read and implement scenario-based data that replaces most human actions.
With so much raw data to sort through in AI development, enterprises can rely on annotation software to simplify the process.
Companies are using the software to better understand and manage their data, which is evident in the following customer reviews:
“We got into a custom project from a client where they own a bunch of stores and they wanted us to create models based on their video analytics data to analyze the behavior of incoming customers, to have a better idea of how people are reacting to certain things that are placed near or farther from them. … I have had experience with different ML programs as well but Amazon Sagemaker stands out to be my favorite.” -Data analyst in the services industry, review of Amazon Sagemaker at Gartner Peer Insights
“Quality annotations by Playment have helped us achieve higher accuracy of our models in a very short time. Flexible solutions, QA process, and a dedicated project manager helped us have peace of mind. The team was able to experience a real off-loading of annotation needs.” -Machine learning specialist in the automotive industry, review of Playment at Playment ’s website .
The data annotation market, as well as the job market for data annotators, has grown with the growth of personal and corporate AI and machine learning applications.
The global annotation software market grew to around $486.1 million in 2020 and is expected to grow at an astounding compound annual rate of 26.9% between 2020 and 2027. Revenue in this market is forecasted to reach $2.57 billion in 2027, according to Grand View Research .
Data annotation software makers
If you’re interested in expanding into AI and machine learning or need additional annotation resources, several companies offer data annotation software/consulting services:
- Amazon SageMaker
- Appen Limited
- Cogito Tech
- Deep Systems
Read next: Top Machine Learning Companies 2021
Similar articles
Stateful inspection firewall: definition, benefits and how it works, artificial intelligence (ai) in supply chains, what is network detection and response (ndr) ultimate guide, latest articles, stateful inspection firewall: definition,..., artificial intelligence (ai) in..., best open source software..., what is a thin....

The Rich Set of Data Annotation and Validation Attributes in .NET
Published in:
Filed under:
- .NET Framework
- SQL Server 2014
- SQL Server 2016
- Stored Procedures
Data annotations are not only for use in ASP.NET web applications. Any type of .NET application can use data annotations for validating data. It only takes about 10 lines of code to programmatically validate data annotations attached to entity classes. There are many built-in data annotations supplied by Microsoft that can validate your data quickly and it's easy to create your own data annotation attributes to apply to your entity classes. If you have some very specific validation needs, you may implement the IValidatableObject interface for your entity classes. If you're developing multilingual applications, you can even move your error messages into resources and specify the name of those resources on each of your attributes.
In this article, you're going to explore most of the data annotations supplied by Microsoft. You're going to develop a few custom validation attributes to check dates and numeric values. You'll also learn to create a custom validation attribute to compare the values between two different properties. You're also going to see how to implement the IValidatableObject interface to tackle more complicated validation scenarios. Finally, you'll set up a couple of resource files and see how easy it is to localize your error messages.
Traditional Validation Methods
In the distant past, to validate the data a user inputs into a form would be done directly in the code-behind the form. The appropriate messages were displayed on the input form to tell the user what they did wrong. As object-oriented programming (OOP) became the norm, developers moved that input data into properties of a class and wrote a Validate() method to perform the validation. A collection of messages is returned from the Validate() method and those messages were bound to the input form to be displayed. Let's first look at the traditional way of validating data before we move onto using data annotations.
Create a Console Application
To follow along with this article, open Visual Studio and create a console application with the name Samples . You may use either .NET 6 or .NET 7 for the samples in this article. Most of the code will work just as well in earlier versions of .NET too. Once you have the application created, right mouse-click on the project and add a new folder named EntityClasses . Right mouse-click on the EntityClasses folder and add a class named Product , as shown in Listing 1 , to this project.
Listing 1: Create a Product entity class to test out validation
There are several properties in the Product class that should be validated, such as making sure the Name property is filled in and that it has 50 characters or fewer in it. You might also verify that the value in the ListPrice property is greater than the value in the StandardCost property. You should also ensure that the value in the SellStartDate property is less than the value in the SellEndDate property.
To report error messages to the user, you need a class to hold the property name in error, and the error message to display to the user. Right mouse-click on the project and add a new folder named ValidationClasses . Right mouse-click on the ValidationClasses folder and add a new class named ValidationMessage . This class is shown in the code snippet below.
Create a Product View Model Class
If you've been doing MVC or WPF programming for a while, you quickly learned that using a Model-View-View-Model (MVVM) design pattern makes your coding easier and more reusable. Let's create a view model class to encapsulate the Product class. Right mouse-click on the project and add a new folder named ViewModelClasses . Right mouse-click on the ViewModelClasses folder and add a new class named ProductViewModel as shown in the code below. Create a public property named Entity that's of the type Product . In the constructor, create a new instance of Product class into the Entity property. Create a Validate() method in which you add code to test for valid Product data. This method returns a list of ValidationMessage objects.
Within the Validate() method, where the comment says to Insert Validation Code Here , write the code shown in Listing 2 to test each of the Product properties for valid data. If the data in a property isn't valid, create a new instance of the ValidationMessage class and put the property name into PropertyName property, and the message you want to convey to the user in the Message property.
Listing 2: Write code to test each property in the Entity object
Open the Program.cs file and add the code shown in Listing 3 . In this code, you create an instance of the ProductViewModel class and fill in a few properties of the Entity property. Call the Validate() method on the ProductViewModel object and it returns a collection of ValidationMessage objects. Iterate over this collection and display each error message on the console.
Listing 3: Test the Product validation in the Program file
Run the program to see the error messages appear on the Console window, as shown in Figure 1 .

Microsoft Data Annotations
Instead of writing validation code in your view model class as you did in Listing 1 , you can add attributes above those properties in your Product class that you wish to validate. There are many standard data annotation attributes supplied by Microsoft such as [Required] , [MinLength] , [MaxLength] , [StringLength] , and [Range] . From the name of the attribute, you can infer what each of these attributes validates on a property. For a complete list of data annotation attributes, visit Microsoft's website at https://bit.ly/3TJICid .
Each of the data annotation attributes inherits from an abstract class named ValidationAttribute . This validation attribute class has properties that each of the inherited attribute classes can use. These properties are shown in Table 1 .
The ErrorMessage Property
The ErrorMessage property is what you use to report back the error message to the user. You can use a hard-coded string or you can have placeholders in the string to automatically retrieve data from the property the attribute is decorating. The placeholders are what the String.FormatString() method uses where you add numbers enclosed within curly braces as shown in the following [Required] attribute.
The {0} placeholder is replaced with the name of the property the attribute is decorating. In the above example, the resulting string is “ProductNumber Must Be Filled In”. Next, look at the following code snippet that uses the [Range] attribute.
The {1} placeholder is replaced with the value in the first parameter to the Range attribute and the {2} placeholder is replaced with the value in the second parameter. If you have more parameters, then you keep on incrementing the placeholder accordingly.
The [Required] Attribute
Let's explore the [Required] attribute in a little more detail and see how to check any properties with this attribute applied. Open the Product.cs file and add the [Required] attribute just above the Name , ProductNumber , StandardCost , ListPrice , and SellStartDate properties as shown in the following code.
If you're using ASP.NET and MVC, the data annotation attributes attached to the properties in a class are automatically validated. If you are using WPF, Windows Forms, or a console application, you need to manually validate those data annotations. There are three classes built into .NET you use to perform this validation. The ValidationContext , the Validator , and the ValidationResult classes are used to generate the error messages from the data annotations. In the example above, you want these classes to return a message that says the user needs to fill in data into those properties decorated with the [Required] attribute. Open the ProductViewModel.cs file, locate the Validate() method and replace all of the code with the code shown in Listing 4.
Listing 4: Use the ValidationContext and Validator objects to validate properties decorated with data annotations
This code creates an instance of a ValidationContext object passing in the Entity property. Create an instance of list of ValidationResult objects so the TryValidateObject() method can fill in this list with all the ValidationResult objects.
The TryValidateObject() method is responsible for checking all data annotations attached to each property in the entity object. If any validations fail, the appropriate error message, along with the property name, is returned in the results variable. Loop through the results collection and add a new ValidationMessage object to the ValidationMessages property. The ErrorMessage property is filled in with the ErrorMessage property from the current ValidationResult item. The property name is retrieved from the first element of the MemberNames property (see Figure 2 ) on the ValidationResult item. It's possible for a data annotation to have two properties to which it applies, but for most simple properties, you only need to grab the first property name.

Open the Program.cs file and modify the view model so the Entity object is initialized with an empty string for both the Name and ProductNumber properties as shown in the following code.
Run the application and you should see the output shown in Figure 3 .
![data annotation resource file Figure 3: The [Required] attribute creates a string with the property name in the error message.](https://codemag.com/Article/Image/2301031/image3.png)
Add ErrorMessage Property to the [Required] Attribute
If you want to change the default error message generated in the ValidationResult object, fill in the ErrorMessage property on the [Required] attribute. Open the Product.cs file and modify the [Required] attributes above the Name and ProductNumber properties to look like the following.
Run the application and you should see the Name and ProductNumber property error messages are different from the StandardCost and ListPrice property error messages as shown in Figure 4 .

The [DisplayName] Attribute
Displaying the property name to the user is generally not a good idea. Sometimes the property name won't mean much to the user. It's better to use a more readable string, such as the same label displayed on an input form. You can accomplish this by adding the [DisplayName] attribute to any property in your class. Open the Product.cs file and add the [DisplayName] attribute above the properties shown in Listing 5 . If the [DisplayName] attribute is attached to a property, the {0} placeholder in the ErrorMessage property uses the Name property from the [DisplayName] attribute instead of the actual property name.
Listing 5: Apply the [DisplayName] attribute to your properties to receive better error messages
After applying the [DisplayName] attribute, run the application and you should now see better error messages, as shown in Figure 5 . The property name is displayed in the parentheses, so you can clearly see the difference.
![data annotation resource file Figure 5: Using the [DisplayName] attribute provides more user-friendly error messages.](https://codemag.com/Article/Image/2301031/image5.png)
Create a Generic Helper Class
Most likely, you're not going to only have a single view model class in your application. Thus, you don't want to write a duplicate of the Validate() method in each of your view models. You can either inherit from a base view model class, or you can create a static class with a method to perform the validation for you. Right mouse-click on the ValidationClasses folder and create a new class named ValidationHelper . Into this new file, replace the code with the code shown in Listing 6 .
Listing 6: Create a generic helper class to perform all the validation for your application
The code in Listing 6 is similar to the code in the Validate() method you wrote in the Product class, but the Validate() method in this code is generic and can accept any type. A new List<ValidationMessage> collection is built each time the Validate() method is called, and it's this list that's returned from this method.
Open the ProductViewModel.cs file and replace the code in the Validate() method with the following code.
Run the application and you should see the same results as previously, but the validation is now happening in the Validate() method in the ValidationHelper class. Each view model class you create from now on just needs this very simple Validate() method. Of course, you can still create a view model base class and move this method into the base class, then have all your view model classes inherit from this base class.
Attributes for Length of Data
There are a few different attributes that you can use to check for the length of string data within your properties. Let's look at the most common data annotations that you're most likely to use.
The [MaxLength] Attribute
The [MaxLength] attribute allows you to specify what the total length the string data within a property should be. If the data within that string property exceeds the specified length, an appropriate error message is returned from the validation. Open the Product.cs file and decorate the ProductNumber and Color properties with the following code.
Open the Program.cs file and modify the initialization of the properties on the Entity object to look like the following.
Run the application and you should see the appropriate error messages displayed for both the ProductNumber and Color properties.
The [MinLength] Attribute
Sometimes you need to ensure a minimum amount of string data is added to a property. For example, the product color shouldn't have any string data that is less than three characters, as there are no one- or two-letter colors. Add a [MinLength] attribute to the Color property in the Product class, as shown in the following code.
Open the Program.cs file and modify the initialization of the Entity object to look like the following code:
Run the application and you should see the appropriate error message displayed for the Color property.
The [String Length] Attribute
If you don't want to apply both the [MinLength] and [MaxLength] attributes to a single property, you may use the [StringLength] attribute as it supports both maximum and minimum length properties. When adding the [StringLength] attribute, the first parameter is the maximum length, and then you can specify the MimimumLength as either the second parameter or as a named parameter, as I have done in the following code. Add the [StringLength] attribute above the Name property in the Product class.
Open the Program.cs file and modify the initialization of the Entity object to look like the following code.
Run the application and you should see the appropriate error message displayed for the Name property. If you want to ensure the maximum length of the Name property also works, try setting the Name property to the following and then run the program again.
The Range Validator
If you have decimal or int properties in your class, you can use the [Range] attribute to check for a minimum and a maximum value that can be entered into those numbers. You may also use a DateTime with the [Range] attribute, but you need to add an additional parameter.

Use the [Range] Attribute with Numeric Values
In the Product class, you have the StandardCost and the ListPrice properties that you should apply a [Range] attribute to. You don't want a cost or a price to be less than zero dollars. Open the Product.cs file and locate the StandardCost and ListPrice properties and add the [Range] attribute as shown below. Be sure to include the ErrorMessage property so you can format the cost as currency.
Run the application and you should see error messages that look like Figure 6 .

Use the [Range] Attributes with DateTime
When using the [Range] attribute with numbers, you specify the minimum and maximum values as the first and the second parameters. When you wish to check a property for a date range, you must pass to the first parameter a typeof(DateTime) so the [Range] attribute class knows to check for a DateTime range. Open the Product.cs file and add a [Range] attribute to the SellStartDate and the SellEndDate properties.
Run the application and you should see error messages that look like Figure 7 .
![data annotation resource file Figure 7: The [Range] attribute can be used with DateTime values as well as numeric values](https://codemag.com/Article/Image/2301031/image7.png)
Regular Expression and Compare Validators
In some classes, you may need the user to adhere to a specific format for the data. For example, phone numbers, social security numbers, etc. The [RegularExpression] attribute can enforce the formatting of data. You need to understand regular expressions to use this attribute, but luckily, there are many resources on the internet to help you with regular expressions. I like www.regexlib.com to look up all sorts of regular expressions. To try out a regular expression, right mouse-click on the EntityClasses folder and add a new class named User that looks like the following code.
Right mouse-click on the ViewModelClasses folder and add a new class named UserViewModel . This class is exactly like the ProductViewModel class in that encapsulates the User class as a property named Entity and has a Validate() method. Enter the code shown below into the UserViewModel.cs file.
The [RegularExpression] Attribute
Let's add a [RegularExpression] attribute to both the EmailAddress and the Phone properties in the User class. When adding the regular expression, don't break them across two lines. I had to break them due to formatting limitations of this printed magazine. I'd highly recommend you include the ErrorMessage property, otherwise, it spits out the regular expression to the user.
Open the Program.cs file and create a new instance of the UserViewModel class and initialize the Entity property with the values shown in the code below so you can test the regular expressions.
Run the application and you should see the error messages, as shown in Figure 8 .

The [Compare] Attribute
A common business rule to enforce is when a user is setting up a new account and they need to put in a new password. It's best to ask them to input that password two times to ensure that they don't misspell the password. In the User class, there's both Password and ConfirmPassword properties. The [Compare] attribute lets you check to ensure that the data contained in both properties is an exact match. Apply the [Compare] attribute to one of the properties and pass in the name of the other property to compare the data to as shown in the following code. It's a best practice to use the nameof() operator so you can rename the property using the Visual Studio rename menu and it will get refactored correctly.
Open the Program.cs file and modify the initialization of the Entity object to look like the following code. Notice the two different values within the Password and the ConfirmPassword properties.
Run the application and the error message you get tells you the names of the properties that don't match, as shown in Figure 9 .
![data annotation resource file Figure 9: The [Compare] attribute compares the data between two properties.](https://codemag.com/Article/Image/2301031/image9.png)
Standard Business Rule Validations
Microsoft realizes that working with regular expressions isn't always the easiest thing to do. They therefore added many attributes to help you enforce the most common business rules such as email, phone, URL, and credit cards. Open the User.cs file and remove the two [RegularExpression] attributes you added to the EmailAddress and Phone properties in the last section.
The [EmailAddress] Attribute
Apply the [EmailAddress] attribute to the EmailAddress property, as shown in the following code.
Apply the [Phone] attribute to the Phone property as shown in the following code.
Open the Program.cs file and modify the initialization of the Entity object to look like the following code. Notice that there's an invalid format for both the EmailAddress and Phone properties.
Run the application and you should see the appropriate error messages for both the email address and phone number properties.
The [Url] Attribute
If you have a URL property in your class, you can use the [Url] attribute to ensure the data contained within that URL is valid. Be aware that the URL entered into your property must start with http://, https://, or ftp://. If you don't want these prefixes, you won't be able to use the [Url] attribute. Open the Product.cs file and add a ProductUrl property and add a [Url] data annotation to it as shown below.
Open the Program.cs file and create a new ProductViewModel object and instantiate the Entity property, as shown in the following code.
Run the application and you should see the error message shown in Figure 10 .
![data annotation resource file Figure 10: The [Url] attribute must contain a valid internet prefix to be considered valid.](https://codemag.com/Article/Image/2301031/image10.png)
The [CreditCard] Attribute
Another common business rule is to check for valid credit card data entered by a user. To try this out, right mouse-click on the EntityClasses folder and add a new class named CreditCard . In the new CreditCard class add the code shown in Listing 7 . Notice the use of the [CreditCard] attribute decorating the CardNumber property.
Listing 7: Create a CreditCard class to test the [CreditCard] annotation
Right mouse-click on the ViewModelClasses folder and add a new class named CreditCardViewModel , as shown in the following code.
Open the Program.cs file and create a new instance of the CreditCardViewModel class and set the appropriate properties of the Entity property, as shown in the following code.
Run the application and you should see an error message informing you that the CardNumber property is not a valid credit card number.
Create Custom Validators Using the [CustomValidation] Attribute
There's no way that Microsoft can anticipate all the needs for business rule validation. They've provided a couple of different methods to create custom validation using attributes. The first method is to use the [CustomValidation] attribute. This attribute accepts two parameters, the first parameter is the type of a class in which you write a static method that returns a ValidationResult object. The second parameter is the name of that static method. Right mouse-click on the ValidationClasses folder and add a new class to your project named WeekdayOnlyValidator and to this new file add the following code:
The Validate() method checks the date passed in to ensure that it doesn't fall on a Saturday or a Sunday. If the date does fall on a weekend, return a ValidationResult object with the error message inside. Otherwise, return a ValidationResult.Success from this method. Right mouse-click on the EntityClasses folder and add a class named Customer . Add the [CustomValidation] attribute to decorate an EntryDate property in the class as shown below.
Right mouse-click on the ViewModelClasses folder and add a new class named CustomerViewModel . Add the code shown in the code snippet below to this new file.
Open the Program.cs file and add a new instantiation of the CustomerViewModel class that sets the EntryDate property of the Entity object to an invalid date.
Run this code and because the date 10/1/2022 falls on a weekend, the validation message “Invalid date because it falls on a weekend.” should appear on your console window.
Create Custom Validators by Inheriting from the ValidationAttribute Class
To me, a better method for performing validation is to inherit from the ValidationAttribute class rather than using the [CustomValidation] attribute. The main reason is that you can name your custom validation class something that's distinct, and easy to read and understand.
There's a common design pattern you use when creating your own custom validation attribute by inheriting from the ValidationAttribute class. The following list describes the things you must do in your validation class.
- Name your class to describe what the validation does.
- Pass to the constructor any parameter(s).
- Override the IsValid() method.
- Validate the data entered is correct.
- Return a ValidationResult object.
- Add an error message if validation is not successful.
- Return ValidationResult.Success if validation is successful.
Is Date Greater Than Minimum Date
A good use of a custom validation attribute is to determine if a date entered by a user is greater than or equal to a specific date. Below is an example of how you might use this attribute to enforce this business rule.
Pass to this custom attribute the minimum date that the data entered by the user should be. If the date entered is this date or greater, then the data is valid. Otherwise, an error message is returned from this attribute, and you can display that message to the user.
Right mouse-click on the ValidationClasses folder and create a new class named DateMinimumAttribute . To this new file, add the code shown in Listing 8 . The first thing you must do is to inherit your class from the ValidationAttribute class. The constructor needs to receive the string that represents the minimum date. You should then convert that string to a DateTime and store it into a read-only private field named _minDate . I'm not performing an error checking to ensure that the date passed is valid, but in production code, you should add error checking.
Listing 8: Create a DateMinimumAttribute class to test for a valid minimum date
Override the IsValid() method to write the code you need to enforce your business rule in your validation class. In this case, I verify that the value passed in is not equal to null. If it isn't null, convert the value entered into the property to a DateTime object. Next, get the name of the property, or the value specified in the [DisplayName] property, so you can use this if you need to return an error message.
Check to see if the date entered is less than or equal to the _minDate field. If it is, return a new ValidationResult object. If the ErrorMessage property is not filled in, create a message to display. The first parameter to the ValidationResult constructor is the error message you wish to display. For the second parameter, create a new string array and fill in the MemberName from the ValidationContext. This MemberName property is the actual property name that this attribute is decorating. If there's no validation error, return a ValidationResult.Success from this attribute class to signify that the data was valid.
Open the Product.cs file and add the DataMinimum attribute to the DiscontinuedDate property, as shown below.
Open the Program.cs file and create an instance of the ProductViewModel class and initialize the Entity property to the following code. Notice that the DiscontinuedDate property is set to a date less than the minimum date specified in the [DateMinimum] attribute.
Run the application and view the error message you get back from the DateMinimumAttribute class.
Is Date Less Than Maximum Date
If you want to try out another custom attribute, copy the DateMinimumAttribute.cs file to a new file in the ValidationClasses folder named DateMaximumAttribute.cs . Open this new file and change the name of the class to DateMaximumAttribute . Rename the _minDate field as _maxDate . Change the comparison operator from a less than or equal sign (<=) to a greater than or equal to sign (>=). Modify the error message to display “less than” rather than “greater than.” You now have another validation attribute that you can use to enforce business rules.
Open the Product.cs file and add to the SellEndDate property the [DateMaximum] attribute, as shown below.
Open the Program.cs file and initialize the Entity property within the ProductViewModel class to the following code. Notice the SellEndDate property is set to a date greater than the maximum date specified in the [DateMaximum] attribute.
Run the application and view the error message you get back from the DateMaximumAttribute class.
Custom Validator: Dynamic Date Range by Year
The problem with the [Range] attribute when working with dates is that the minimum and maximum dates you enter must be hard-coded strings. What if you want to make the range a little more dynamic? For example, you might want to specify the minimum year the user may enter is two years prior to today's date. And the maximum year the user may enter is five years after today's date. To accomplish this, create a [DateYearRange] attribute class and pass in two integer values that specify the years prior and after that are valid for the date entered.
Right mouse-click on the ValidationClasses folder and add a new class named DateYearRangeAttribute . Into this file add the code shown in Listing 9 . In the constructor, you accept the integer values and use those to calculate the two private read-only fields: _minDate and _maxDate .
Listing 9: Create a DateYearRangeAttribute class to test for a valid date within two date ranges by year
Within the IsValid() method, retrieve the value entered from the user, then check to see if that date entered is less than the _minDate field or if it's greater than the _maxDate field. If either of these conditions fail, return a ValidationResult object with the error message telling the user the date range their input value must fall within.
Open the Product.cs file and add to the SellStartDate property the [DateYearRange] attribute, as shown below.
Open the Program.cs file and initialize the Entity property within the ProductViewModel class to the following code. Notice that the SellStartDate property is set to six years prior to today's date. This will cause the [DateYearRange] attribute to fail the validation.
Run the application and view the error message you get back from the DateYearRange class.
Custom Validator: Is One Date Property Less Than Another
Earlier in this article, you learned about the [Compare] attribute, which allows you to check if the data in one property is equal to the data in another. Let's create a validation attribute that does something similar but checks to see if one date value is less than another value. For example, you might want check to see if the SellStartDate is less than the SellEndDate property in the Product class.
Right mouse-click on the ValidationClasses folder and add a class named CompareDateLessThanAttribute.cs . Replace all the code in this file with the code shown in Listing 10. The constructor accepts the name of the property to compare to as a string value. Place this value into a private read-only field named _propToCompare . Retrieve the value entered by the user from the value parameter passed in and convert it to a DateTime type. Use the GetProperty() method on the ValidationContext.ObjectType object to retrieve the actual address of where the property to compare to is located in memory. Once you have the PropertyInfo object call the GetValue() method to get the value in the property to compare to. If that value is not null, use that value to perform the comparison to the current value so you know whether to return an error message.
Listing 10: Create a CompareDateLessThanAttribute class to test for one date must be less than another date
Open the Product.cs file and add to the SellStartDate property the [CompareDateLessThan] attribute, as shown below. The first parameter to the attribute is the name of the property you want to compare it to.
Open the Program.cs file and initialize the Entity property within the ProductViewModel class to the following code. Notice that the SellEndDate property is set to one day prior to the SellStartDate . This causes the [CompareDateLessThan] attribute to fail the validation.
Run the application and view the error message you get back from the CompareDateLessThan class.
Custom Validator: Is One Numeric Property Less Than Another
If you want to check if a numeric property is less than another numeric property, copy the CompareDateLessThanAttribute.cs file to a new file named CompareDecimalLessThanAttribute.cs . Open this new file and change the name of the class to CompareDecimalLessThanAttribute . Change all instances of DateTime to decimal. You now have another validation attribute that you can use to ensure that one decimal property is less than another.
Open the Product.cs file and add to the StandardCost property the [CompareDecimalLessThan] attribute, as shown in the code below. The first parameter to the attribute is the name of the property you want to compare it to.
Open the Program.cs file and initialize the Entity property within the ProductViewModel class to the following code. Notice that the ListPrice property is set to a value less than the value in the StandardCost property. This causes the [CompareDecimalLessThan] attribute to fail the validation.
Run the application and view the error message you get back from the CompareDecimalLessThan class.
Implement the IValidatableObject Interface
If you have business rules that are very specific to a class, you may not want to inherit from the ValidationAttribute. Instead, you might want to keep the code that performs the validation within the class itself. To do this, implement the IValidatableObject interface on your class. Right mouse-click on the EntityClasses folder and add a new class named Employee . Replace all the code in this new file with the code shown in Listing 11 .
Listing 11: Instead of using data annotations, you may implement the IValidatable interface to check properties on your entity classes
This Employee class defines several properties and implements the Validate() method. Within the Validate() method is where the business rules are implemented. This is a simple example, and these rules could have been implemented using data annotations, but I wanted to show you how this method works.
One thing to be aware of is that if you have both data annotations and this interface, the [Required] data annotations are validated first. Once all the [Required] annotations are resolved, other data annotations are then validated. The Validate() method on your class is not called until there are no ValidationResult objects passed back from the TryValidateObject() method in the ValidationHelper class. Then, and only then, is the Validate() method on your class called.
Right mouse-click on the ViewModelClasses folder and add a new class named EmployeeViewModel . This class contains an Entity property that's of the type Employee and has a Validate() method used to check the business rules of the Employee object.
Open the Program.cs file and instantiate the EmployeeViewModel class and set the appropriate properties on the Entity object as shown in the following code.
Run the application and view the validation messages.
Localizing Error Messages
If you need to work with multiple languages such as English, Spanish, German, etc. You should not hard-code error messages in the data annotation attributes. Instead, move them to a resource file and use the ErrorMessageResourceName property on the data annotation instead of the ErrorMessage property. You also need to include the ErrorMessageResourceType property to specify the namespace and class where the resources are compiled. Let's look at how to set up resources, add both English and Spanish error messages, and connect those messages to data annotations.
Create Resources
Add one resource file per language that you wish to support in your application. Right mouse-click on your project and create a new folder named Resources . Right mouse-click on the Resources folder and select Add > New Item from the menu. Locate the Resources File template and set the name to ValidationMessages.resx . Click the Add button to add the new resource file to the project, as shown in Figure 11 .

After adding the resources file, you need to change the Access Modifier to Public , as shown in Figure 12 . Once you set this modifier, a ValidationMessages class is generated by Visual Studio so you can access each resource as a property of that class.

Table 2 is a list of the resources you need to add to the ValidationMessages file. Each of these resources correspond to the similarly named data annotation attribute. These resource names show up as properties in the ValidateMessage class.
Create a Spanish Version of Error Messages
You're going to learn how to assign the resource names to the data annotation attributes in your Product class, but before you do that, let's create the Spanish version of these error messages. Right mouse-click on the Resources folder and select Add > New Item from the menu. Locate the Resources File template and set the name to ValidationMessages.es-MX.resx . Click the Add button to add the new resource file to the project. Add the same names as you did in the first resource file you added. The Value property is what changes for each language. In Figure 13 , you see the values you should enter for the Spanish error messages. Please excuse any bad Spanish grammar as I used “Google Translate.”

Why did you add the suffix of “.es-MX” on this file, but didn't use one on the other resource file? The resource file selected is based on two things; the culture running on the computer and the culture set on the current thread. If the two cultures match, the resource file without a suffix is chosen, otherwise the resource file that matches the culture on the current thread is selected.
Modify Product Class to Work with Resources
Let's now modify the Product class to use the values from the resource files instead of the hard-coded messages you've used throughout this article. Open the Product.cs file and add a Using statement at the top of this file. This Using statement is the namespace where the ValidationMessages class has been generated.
Locate the Name property and modify the [Required] and [StringLength] attributes. Remove the ErrorMessage property from each of these attributes and add the ErrorMessageResourceName and ErrorMessageResourceType properties, as shown in the following code:
Next, modify the [MaxLength] attribute on the ProductNumber property to look like the following code:
Finally, modify the [MinLength] property on the Color property to look like the following:
Open the Program.cs file and instantiate the ProductViewModel class to look like the following:
Run the application to see the English language messages from the resource file appear.
Modify the Culture on the Current Thread
Open the Program.cs file and add a using statement at the top of this file.
Add a new variable named culture to set the current language.
Just before the call to the vm.Validate() method, set the current UI culture to the string contained in the culture variable. The culture set on the CurrentUICulture thread is the one used to determine which resource file to use.
Run the application again and you should see the same English language messages appear. After viewing the error messages, modify the culture variable to “es-MX” as shown below:
Run the application again to see the Spanish language messages appear.
Modify the User Class to Work with Resources
In the User class, you added the [EmailAddress] and the [Phone] attributes. You should add the appropriate error messages for those two attributes to each of your resource files. Open the ValidationMessages.resx file and add the name/value pairs shown in Table 3 .
Open the ValidationMessages.es-MX.resx file and add the name/value pairs shown in Table 4 .
Open the User.cs file and add a using statement at the top of the file to bring in the namespace where the ValidationMessages class is located.
Add a [Required] attribute to the LoginId property to look like the following code:
Modify the [EmailAddress] attribute on the EmailAddress property to look like the code shown below:
Modify the [Phone] attribute on the Phone property as shown in the code below:
Open the Program.cs file and instantiate the UserViewModel to look like the following:
Be sure to set the culture variable back to “en-US” and run the application to see the English language messages from the resource file appear. After running the English version, change the culture variable to “es-MX” and run the application to see the Spanish language version of the error messages.
In this article, you learned about many of the data annotations available in .NET. There are more annotations than what I covered in this article, but I covered the ones you're going to use most often. There's great functionality that you get out of the data annotations available from Microsoft, but if they don't cover your needs, it's very easy to build your own validation attributes. The custom attributes illustrated in this article should provide you with a good start. Take advantage of resource files, even if you're not doing multilingual applications. They're great for ensuring that all your error messages stay consistent from one class to another.
Table 1: The common properties available to all data annotation attribute classes
Table 2: add a resource name and value for each message you wish to display in your application, table 3: add resources for the `[emailaddress]` and `[phone]` attributes to the `validationmessages.resx` file, table 4: add resources for the `[emailaddress]` and `[phone]` attributes to the `validationmessages.es-mx.resx` file, this article was filed under:, this article was published in:.

Have additional technical questions?
Get help from the experts at CODE Magazine - sign up for our free hour of consulting!
Contact CODE Consulting at [email protected] .
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Use file data with Attachment and Note records
- 10 minutes to read
- 1 contributor
Attachment (ActivityMimeAttachment) and Note (Annotation) tables contain special string columns that store file data.
These tables existed before file or image columns, so they work differently.
- The binary file data is stored as Base64 encoded string values in string columns: ActivityMimeAttachment.Body and Annotation.DocumentBody .
- File name data is stored in the FileName column.
- Mime type data is stored in the MimeType column.
Because these columns are part of the data for the attachment or note record, you should update these three columns together with any other values.
Using file data
You can directly get and set the values of the activitymimeattachment.body and annotation.documentbody columns as Base64 encoded strings. This should be fine as long as the files are not too large, for example under 4 MB.
By default the maximum size is 5 MB. You can configure these columns to accept files as large as 128 MB. When you have increased the maximum file size and are working with larger files, you should use messages provided to break the files into smaller chunks when uploading or downloading files. For information about retrieving or changing the file size limits, see File size limits .
Attachment files
An attachment is a file that is associated with an email activity, either directly or though an Email Template (Template) . Multiple attachments can be associated with the activity or template.
You can re-use attachment files by setting the activitymimeattachment.attachmentid value to refer to another existing attachment rather than by setting the body , filename , and mimetype properties.
Attachment (ActivityMimeAttachment) should not be confused with activityfileattachment , which supports files associated with the Post table.
Within the Dataverse schema there is also a public table with the name Attachment which is exposed in the Web API as attachment EntityType . This table can be queried and it reflects data in the ActivityMimeAttachment table. But it doesn't support create, retrieve, update, or delete operations. This table doesn't appear within the PowerApps designer.
Upload Attachment files
Use the InitializeAttachmentBlocksUpload , UploadBlock , and CommitAttachmentBlocksUpload messages to upload large files for attachments.
These messages can only be used to create a new attachment. It you try to update an existing attachment with these messages you will get an error that the record already exists.
- SDK for .NET
The following static UploadAttachment method shows how to create a new attachment with a file using the InitializeAttachmentBlocksUploadRequest , UploadBlockRequest , and CommitAttachmentBlocksUploadRequest classes to return a CommitAttachmentBlocksUploadResponse with ActivityMimeAttachmentId and FileSizeInBytes properties.
More information:
- Use the Organization service
- IOrganizationService.Execute Method
This example method includes some logic to try to get the MIME type of the file using the FileExtensionContentTypeProvider.TryGetContentType(String, String) Method if it isn't provided. If not found it will set the mime type to application/octet-stream .
The following series of requests and responses show the interaction when using the Web API to create a new attachment record that sets the body , filename , and mimetype columns for a PDF file named 25mb.pdf . The attachment is associated to an email.
The first request uses the InitializeAttachmentBlocksUpload Action .
The response is a InitializeAttachmentBlocksUploadResponse ComplexType providing the FileContinuationToken value to use with subsequent requests.
You must then break up the file into blocks of 4 MB or less and send each block using the UploadBlock Action with the following properties:
With .NET, you can generate a BlockId using this code:
Also with .NET, if you set the byte[] data to a JObject BlockData property, the byte[] will be Base64 encoded when you set the HttpRequestMessage.Content using JObject.ToString() .
After all the parts of the file have been sent using multiple requests using the UploadBlock Action , use the CommitAttachmentBlocksUpload Action to complete the upload using these properties:
In this example the BlockList values represent seven previous requests using the UploadBlock Action .
Download Attachment files
You can download an attachment file in a single operation using the Web API, or you can download the attachment file in chunks using the SDK or Web API.
Download Attachment files in a single operation using Web API
Using the Web API, you can download an attachment file in a single operation:
Unlike retrieving file columns, this method does not provide information about file size, file name, or mime type. More information: Download a file in a single request using Web API and Retrieve the raw value of a property
Download Attachment files in chunks
To retrieve the file in in chunks, you must use the following, with either the SDK or Web API:
Once you've downloaded all the blocks, you must join them to create the entire downloaded file.
The following static DownloadAttachment method shows how to download an attachment using the SDK with the InitializeAttachmentBlocksDownloadRequest and DownloadBlockRequest classes. This function returns the byte[] data and the name of the file.
The following series of requests and responses show the interaction when using the Web API to download a PDF file named 25mb.pdf from the an attachment with the specified activitymimeattachmentid value.
This request uses the InitializeAttachmentBlocksDownload Action .
The response is a InitializeAttachmentBlocksDownloadResponse ComplexType which provides:
- FileName : The name of the file
- FileSizeInBytes : The size of the file
- FileContinuationToken : The file continuation token to use in subsequent requests
Based on the size of the file and the size of the block you'll download, send more requests using the DownloadBlock Action as shown below.
With each request, increment the Offset value by the number of bytes requested in the previous request. For example, the following values were used to download a file that is 25870370 bytes in seven requests:
The requested BlockLength value can be constant. It isn't required to be adjusted for the last request in the example above where only 704546 bytes remained.
More information: Use Web API actions
Annotation files
A note is a record associated with a table row that contains text and may have a single file attached. Only those tables defined with EntityMetadata.HasNotes set to true may have notes associated with them.
Upload Annotation files
Use the InitializeAnnotationBlocksUpload , UploadBlock , and CommitAnnotationBlocksUpload messages to upload files for notes.
The annotation you pass as the Target parameter for these messages must have an annotationid value. This is how you can update existing annotation records.
To create a new annotation with these messages you must generate a new Guid value to set as the annotationid value rather than let Dataverse generate the value. Normally, it is best to let Dataverse generate the unique identifier values when creating new records, but that is not possible with these messages.
The following static UploadNote method shows how to create or update a note with a file using the InitializeAnnotationBlocksUploadRequest , UploadBlockRequest , and CommitAnnotationBlocksUploadRequest classes and it will return a CommitAnnotationBlocksUploadResponse with AnnotationId and FileSizeInBytes properties.
The following series of requests and responses show the interaction when using the Web API to create a new annotation record that setting the documentbody to a PDF file named 25mb.pdf . The annotation is associated to an account record.
The first request uses the InitializeAnnotationBlocksUpload Action .
The response is a InitializeAnnotationBlocksUploadResponse ComplexType providing the FileContinuationToken value to use with subsequent requests.
After all the parts of the file have been sent using multiple requests using the UploadBlock Action , use the CommitAnnotationBlocksUpload Action to complete the upload using these properties:
Download Annotation files
You can download a note file in a single operation using the Web API, or you can download the note file in chunks using the SDK or Web API.
Download Annotation files in a single operation using Web API
Using the Web API, you can download an note file in a single operation:
Download Annotation files in chunks
The following static DownloadNote method shows how to download an note using the SDK with the InitializeAnnotationBlocksDownloadRequest and DownloadBlockRequest classes. This function returns the byte[] data and the name of the file.
The following series of requests and responses show the interaction when using the Web API to download a PDF file named 25mb.pdf from the a note with the specified annotationid value.
This request uses the InitializeAnnotationBlocksDownload Action .
The response is a InitializeAnnotationBlocksDownloadResponse ComplexType which provides:
File size limits
The Organization.MaxUploadFileSize column specifies the maximum allowed size of an a file (in bytes) for an attachment and note, as well as other kinds of data, such as web resource files used for model-driven apps.
The default size is 5 MB (5242880 bytes) and the maximum value is 128 MB (131072000 bytes) and can be set in the email settings for the environment. More information: Manage email settings
If you try to upload a file that is too large, you'll get the following error:
Name: unManagedidsattachmentinvalidfilesize Code: 0x80044a02 Number: -2147202558 Message: Attachment file size is too big.
The maximum upload file size limit applies to the size of the file in Base64 encoding. A Base64 encoding produces a string that is larger than the original byte[] file data..
Retrieve max upload file size
Use the following ways to retrieve the maximum upload file size.
Use a static method like the following GetMaxUploadFileSize to get the value.
- IOrganizationService.RetrieveMultiple Method
- Build queries with QueryExpression
Return the single row from the organization table with the maxuploadfilesize property.
Basic query example
Update max upload file size
Use the following to set the organization.maxuploadfilesize value.
Use a static method like the following SetMaxUploadFileSize to set the maximum upload file size.
- IOrganizationService.Update Method
There is only a single row in the organization table. After you retrieve that value, use the organizationid returned to update the row. The WhoAmIResponse also includes this value.
More information: Basic update
Files and images overview Sample: File operations with Attachments and Notes using the Dataverse SDK for .NET Sample: Attachment and Annotation file operations using Dataverse Web API
Submit and view feedback for
Additional resources
Localising Data Annotation Attributes in Razor Pages
This is the third article in a series that explores various aspects of localisation in ASP.NET Core Razor Pages applications. The first article looked at how to work with cultures , and the second covered the basics of using resource files for static content translations . In this article, I explain how to provide localised resources for form labels and validation error messages for PageModel properties that have Data Annotation attributes applied to them.
The application in this article is the same one that has featured in the previous articles. It's built using the standard Razor Pages 3.1 project template with no authentication. Many of the concepts in this article were originally introduced in the previous article, so you should read that first.
The first three steps that follow demonstrate the minimum configuration to enable localisation using resources. If you are continuing from the previous article, you will have covered those:
In ConfigureServices , localization is added to the DI container, specifying the location of resources in the application, and the cultures supported by the application are configured:
The CommonLocalizationService is a wrapper around an IStringLocalizer<T> which is used to access resource files. It was introduced as a means of accessing page-agnostic resource files in the previous article.
Localisation middleware is added after routing, passing in the localisation options specified in the previous step, in the Configure method:
A folder named Resources is added to the root of the application, containing an empty class named CommonResources :
Resources are accessed using a localization provider ( IStringLocalizer<T> ) which needs to work with a specific type. If the resources are intended to be used in just one page, you can use the PageModel class as the type. Translations for data annotations should be made available for use in more than one page, so they need to be set up to be page-agnostic. The CommonResources class provides a page-agnostic type for the resources. It's empty (has no members) because it is just a placeholder.
In the previous article, the AddViewLocalization extension method is used to add the view localisation services to the application's service collection. In this article, the AddDataAnnotationsLocalization extension method is chained to enable configuration of the IStringLocalizer to be used for accessing resources that contain data annotation translations. A factory is used to create an IStringLocalizer which is typed to the empty CommonResources class created in the last article to support global or page-agnostic resource files.
The example that follows demonstrates the use of data annotations on PageModel properties that represent values posted from a form. The form is a simple contact form, in which all the form fields are required.
Add a new Razor page to the application named Contact.cshtml
Add the following using directive to the top of the PageModel file:
Add the following properties with data annotation attributes to the ContactModel:
I haven't included any handler methods in this example because the focus is not on processing posted form values.
This step builds on the shared resource files that were introduced in the previous article. Translations for the labels and the error messages are added to the English, French and German resources, along with entries for navigation to the Contact page and the submit button on the form. Only the additional entries for German resource file ( CommonResources.de.resx ) are shown here for brevity:
The keys for each entry are the values passed to the Name property of the Display attribute, and the ErrorMessage property of the Required attribute. The French resource file ( CommonResources.fr.resx ) needs translations for most of the same keys as the German one, except for the words Contact and Message , which are the same in French as in English. The English resource file needs "translations" for the error messages, unless you are happy with the existing (fairly concise) values assigned within the attributes.
In addition to the data annotation entries, there are three further entries. These are for the navigation, the title on the contact page and the button that will be used to submit the contact form.
The navigation can be added to the layout page, using the CommonLocalizerService that was created and injected into the layout page in the last article:
Finally, the form can be created in Contact.cshtml :
The CommonLocalizerService is injected into the page, and is used to provide the translations for the page title, heading and the submit button. The rest of the form could be taken from any application. It uses standard tag helpers for labels, inputs and validation messages. Unobtrusive validation is enabled through the inclusion of the ValidationScriptsPartial file.
The final touch is to add some styles to the site.css file, in wwwroot/css to add some colour to inputs and messages in the event of validation failures:
If you run the application and navigate to the contact page, you can test the localisation simply by trying to submit the empty form. The client side validation should kick in since none of the required fields have values:

Then you can use the culture switcher to test translations:

This article demonstrates how to localise data annotation attributes in a Razor Pages application. The process is based on the use of resources and requires its own configuration.
So far, the culture for a request has been set as a query string value via the Culture Switcher view component that was created in the first article in the series. This is not a recommended approach . In the next article, I will look at how you can manage cultures via Route Data instead .
You might also like...
Other sites.
- Learn Razor Pages
- Learn Blazor

- ADO.NET (24)
- ASP.NET 2.0 (39)
- ASP.NET 3.5 (43)
- ASP.NET 5 (16)
- ASP.NET Core (70)
- ASP.NET Identity (3)
- ASP.NET MVC (90)
- ASP.NET Web Forms (32)
- ASP.NET Web Pages (89)
- Blazor (11)
- Book Review (7)
- Bootstrap (4)
- Classic ASP (13)
- Entity Framework (34)
- Extension Method (6)
- General (12)
- iTextSharp (12)
- Javascript (22)
- jQuery (34)
- Localization (4)
- MS Access (17)
- Razor Pages (49)
- SQL Server Express (2)
- TypeScript (1)
- VB.Net (29)
- VBScript (11)
- Visual Studio (5)
- Web API (1)
- WebGrid (16)
- WebMatrix (80)
- January 2023 (1)
- November 2022 (3)
- October 2022 (1)
- September 2022 (1)
- August 2022 (1)
- December 2021 (1)
- October 2021 (1)
- July 2021 (2)
- November 2020 (3)
- June 2020 (2)
- May 2020 (1)
- January 2020 (1)
- December 2019 (3)
- November 2019 (3)
- October 2019 (2)
- August 2019 (2)
- July 2019 (2)
- May 2019 (1)
- April 2019 (2)
- March 2019 (1)
- February 2019 (1)
- October 2018 (3)
- September 2018 (4)
- August 2018 (2)
- July 2018 (2)
- May 2018 (2)
- March 2018 (2)
- February 2018 (1)
- January 2018 (1)
- September 2017 (2)
- July 2017 (2)
- May 2017 (3)
- February 2017 (2)
- October 2016 (2)
- September 2016 (1)
- July 2016 (2)
- June 2016 (1)
- May 2016 (1)
- April 2016 (1)
- March 2016 (2)
- February 2016 (1)
- January 2016 (2)
- December 2015 (3)
- October 2015 (3)
- September 2015 (2)
- August 2015 (2)
- July 2015 (4)
- June 2015 (2)
- May 2015 (2)
- April 2015 (4)
- March 2015 (5)
- February 2015 (4)
- January 2015 (4)
- October 2014 (2)
- August 2014 (1)
- July 2014 (1)
- June 2014 (12)
- May 2014 (11)
- April 2014 (1)
- March 2014 (2)
- February 2014 (2)
- January 2014 (1)
- December 2013 (1)
- November 2013 (2)
- October 2013 (2)
- August 2013 (3)
- July 2013 (1)
- June 2013 (1)
- May 2013 (1)
- February 2013 (3)
- January 2013 (2)
- December 2012 (4)
- November 2012 (1)
- October 2012 (1)
- September 2012 (5)
- August 2012 (2)
- July 2012 (2)
- June 2012 (3)
- May 2012 (1)
- February 2012 (1)
- January 2012 (1)
- December 2011 (2)
- October 2011 (1)
- September 2011 (1)
- August 2011 (6)
- May 2011 (1)
- April 2011 (1)
- March 2011 (2)
- January 2011 (5)
- December 2010 (3)
- October 2010 (4)
- September 2010 (2)
- August 2010 (2)
- July 2010 (9)
- June 2010 (2)
- May 2010 (7)
- April 2010 (1)
- March 2010 (1)
- February 2010 (4)
- January 2010 (2)
- December 2009 (4)
- November 2009 (2)
- October 2009 (4)
- September 2009 (3)
- August 2009 (1)
- July 2009 (2)
- June 2009 (4)
- May 2009 (3)
- April 2009 (1)
- March 2009 (1)
- February 2009 (2)
- January 2009 (5)
- December 2008 (4)
- November 2008 (5)
- October 2008 (6)
- July 2008 (1)
- May 2008 (3)
- April 2008 (2)
- November 2007 (5)
- September 2007 (1)
- August 2007 (8)
- July 2007 (2)
- June 2007 (3)
- May 2007 (20)
- April 2007 (14)
- March 2007 (3)

IMAGES
VIDEO
COMMENTS
The resx file has Embedded Resources as BuildAction. The name for the resx file is composed with the namespace of the class. For example, if the class is named SharedResources inside the namespace Dto. then your resource files is named Dto.SharedResource.it.resx. Conventions I suppose. - Steve Jul 4, 2018 at 15:20
Creating Resource File and Configuring Model Class To add a resource file into your solution, right click project in solution explorer and click Add > New Item.. In the Add New Item dialogue, select "Resources File" under General category. Rename the file name to EmployeeResx.resx and Click Add.
In this post you will learn how to create custom data annotation in MVC. MVC framework has great extensibility feature and because of this we can create our own customized data annotation attributes. Custom Data Annotation Validation In MVC 1/20/2021 5:22:36 AM. In this article you will learn about Data Annotations and Validation in MVC.
Make sure the resource values in the resource file properties are public and not internal. By default they are internal. To use these properties in data annotation, they need to be made public. open the .resx file and select the properties (Error resource name) and set the access modifier to public (u can see that on the top).
DataAnnotation has ErrorMessageResourceType and ErrorMessageResourceName properties but ErrorMessageResourceType only accepts System.Type (i.e. a compiled resource file) Is there any way to get DataAnnotation to use the custom ResourceProvider? c# asp.net-mvc localization resources Share Follow edited Jan 22, 2014 at 15:52 analyticalpicasso
This code functions as expected: [Display (Name = "LastNameDisplay", ResourceType = typeof (Web.Resources.DemographicsServiceResources))] This code functions as expected: [EmailValidator (ErrorMessageResourceName = "EmailFormatInvalid", ErrorMessageResourceType = typeof (Web.Resources.DemographicsServiceResources))] This code does not function.
This article provides an overview of validation in ASP.NET MVC 5 using a Resource file. Creating Custom Validation Attribute For Data Annotation 6/27/2012 8:52:25 PM. In .net 4.0, namespace System.ComponentModel.DataAnnotations allow you to create new attribute and with the help of this you can validate the data as per your requirement.
I have a corresponding SharedResource.en-US.resx and SharedResource.fi-FI.resx file in my Resources folder. This means any annotation localizations will now come from those files! The Finnish RESX file contains values like this: Name: Nimi {0} is required: {0} on pakollinen; Then if we have a model, we can do annotations like this:
Select both the Microsoft.Web.Mvc.DataAnnotations.dll assembly and the System.ComponentModel.DataAnnotations.dll assembly and click the OK button. You cannot use the System.ComponentModel.DataAnnotations.dll assembly included with .NET Framework Service Pack 1 with the Data Annotations Model Binder.
Data annotation is the primary solution that bridges the gap between sample data and AI/machine learning. Data annotation is a process where a human data annotator goes into a raw data set and adds categories, labels, and other contextual elements, so machines can read and act upon the information.
Data annotations are property attributes like Display, Required, StringLength, ...etc. Localizing data annotations made easy with ASP.NET Core 2.1 using shared resource files. ... Well, in the documentations it looks a bit simpler, but this way the resource files can be defined in another project like a class library project that can be shared ...
Data annotations are not only for use in ASP.NET web applications. Any type of .NET application can use data annotations for validating data. It only takes about 10 lines of code to programmatically validate data annotations attached to entity classes.
In this article. Attachment (ActivityMimeAttachment) and Note (Annotation) tables contain special string columns that store file data. These tables existed before file or image columns, so they work differently. The binary file data is stored as Base64 encoded string values in string columns: ActivityMimeAttachment.Body and Annotation.DocumentBody. File name data is stored in the FileName column.
Translations for data annotations should be made available for use in more than one page, so they need to be set up to be page-agnostic. The CommonResources class provides a page-agnostic type for the resources. It's empty (has no members) because it is just a placeholder.