Code and prod logo

Code and Prod Complet Projects

Follow me

EL JIHAOUI
QUICK ACCESS

Code First data annotations

Code First data annotations in .net 7

Table of Contents

in this article, we will see what data annotations are and how they can be used in entity framework core to protect, control, and also ensure the consistency and validity of data.

Code First Approach

Code First approach in Entity Framework core allows us to use our domain classes to represent and configure the model that ef core relies on to perform querying, track all changes, and perform CRUD operations using the famous classes DbContext And DbSet.

Code First assumes that your class follows Entity Framework conventions, and if so, automatically determines how to do its job. However, we can add configuration to our class to give Entity Framework the information it needs. 

Code First gives us two ways to add these configurations to our classes. the first one is using simple attributes called Data Annotations, and the second is using Code First’s Fluent API, which forces us to describe the configurations in our code. we will focus in this article on data annotations

Data Annotations

In .Net 7, Data Annotations attributes are attributes and constraints that can be applied to an entity class/Model or properties, these attributes are used to control and validate input data either at the user interface level or at the database level, and can also be used to add a configuration layer when creating the model.

Data annotation attributes are included in the System.ComponentModel.DataAnnotations and System.ComponentModel.DataAnnotations.Schema namespaces

Data annotations useful attributes

AttributeDescription
KeyApplied to a property to specify a key property in an entity and make it a Primary Key column in the database.
RequiredUsed to specify that the corresponding column is a NotNull column in the database.
MinLengthSpecify the minimum string length allowed in the corresponding column in the database.
MaxLengthSpecify the maximum string length allowed in the corresponding column in the database.
StringLengthUsed to specify the maximum string length allowed in the corresponding column in the database. we can combine with this attribute the minimum and the maximum length like this : [StringLength(30, MinimumLength = 7)]
RangeSpecify the numeric range constraints for the value of a property.

Data annotations Schema useful attributes

AttributeDescription
TableApplied to an entity class to configure the corresponding table name and schema in the database.
ColumnColumn annotations are a better way to indicate the attributes of mapped columns. You can set the name, data type, and even the order in which columns appear in the table.
IndexIndex Attributes can be used to create an index on one or more columns. Adding the attribute to one or more properties ensures that EF will either create the appropriate indexes in the database when it’s created
ForeignKeyA ForeignKey DataAnnotation allows the code first understand how the relationship between the two classes is constructed and how the constraints are specified in the database.
NotMappedYou can mark any properties that do not map to the database with the NotMapped annotation. if used, that property can be created dynamically and does not need to be stored in the database.
Example: FullName property

Demo of Using Data Annotations

in this demonstration, we will apply some constraints to our class Author using some attributes mentioned above :

				
					 [Table("tb_Author", Schema = "bk")]
    public class Author
    {
        [Key]
        [Column("AuthorId")]
        public int Id { get; set; }

        [Required]
        [Column(TypeName = "varchar(50)")] 
        public string? FirstName { get; set; }

        [Required]
        [Column(TypeName = "varchar(60)")] 
        public string? LastName { get; set; }
        
        [NotMapped]
        public string FullName => $"{FirstName} {LastName}";
    }
				
			

after executing the migration and updating the database, we will create the table tb_Author with the schema bk , with respect also the constraints and attributes mentioned in the code above.

therefore we will see in our database the following table :

Data-Annotations

Using Data Annotations with Console Application

Normally we use Data Annotations when we want to validate the data at the user interface level (input forms) or the database level. but also we can use these data annotations attributes within a console application with the exploit of an object named Validator in the namespace System.ComponentModel.DataAnnotations.

We will take the following example to show how to use the Validator object to validate or not the data before inserting them into the database.

we consider the following class that represents a Book with some data annotations attributes :

				
					using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;

namespace GetStarted_EF.Models
{
    public class Book
    {
        [Key]
        public int Id { get; set; }

        [Required(ErrorMessage ="Name is required !!!")]
        [StringLength(30, MinimumLength = 3,ErrorMessage = "the name must contain at least 3 characters")]
        public string? Name { get; set; }

        [Range(1,200,ErrorMessage ="NbPages must be between 1 and 200")]
        public int NbPages { get; set; }
        [Comment("Description of the Book")]
        public string? Description { get; set; }
    }
}
				
			

In the Program.cs we will add the following code for testing the validation functionality like that :

				
					using System.ComponentModel.DataAnnotations;
using GetStarted_EF.Data;
using GetStarted_EF.Models;
using static System.Console;
AppDbContext db = new();
if (db != null)
{
    Book book = new() { Name = "A", NbPages = 300, Description = "aa" };
    ValidationContext context = new(book);
    List<ValidationResult> validationsResults = new();
    bool IsValid = Validator.TryValidateObject(book, context, validationsResults, true);
    if (!IsValid)
    {
        foreach (ValidationResult validation in validationsResults)
        {
            WriteLine($" - {validation.ErrorMessage}");
        }
    }
    else
    {
        if (db.Books != null)
            await db.Books.AddAsync(new Book() { Name = "A", NbPages = 300, Description = "aa" });
        bool res = await db.SaveChangesAsync() > 0;
        if (res)
        {
            WriteLine("Book Inserted successfully");
        }
        else
        {
            WriteLine("Book not Inserted , somethings went wrong");
        }
    }
}
				
			

If we execute this code with dotnet run command, We are going to invoke the Validator.TryValidateObject method to test whether the Book object book = new() { Name = “A”, NbPages = 300, Description = “aa” } respects the constraints of the data annotations or not.

As a result, the execution displays the following error messages :

  • The name must contain at least 3 characters
  • NbPages must be between 1 and 200

And the book object will not be inserted into the database because it doesn’t respect the data annotations attributes mentioned in the book class.

however, if we test the add operation of the object Book book1 = new() { Name = “Programming with C#”, NbPages = 180, Description = “Learn C#” }, the insertion in the database will be achieved successfully because this object respects the instructions imposed by data annotations.

  • Book name contains at least 3 characters and its length is less than 30 characters;
  • NbPages property is between 1, and 200 as the possible data range for this integer.

N.B: Data annotations provide only a subset of configuration options that can be applied to our classes. However, Fluent API provides a complete set of configuration options for the Code-First approach.  

Course Video

if you like it share it please

Facebook
LinkedIn
Twitter
Related Posts
Data Seeding in migration using EF Core
Entity Framework Core
EL JIHAOUI

Data seeding

Data seeding is a process where you populate a database with an initial set of data
There are

Read More »

MORE RESSOURCES