EF Core Code First Migrations With DataAnnotations Attributes

by GeeksArray

In this blog post you will use DataAnnotations to configure pre-defined rules and constraints for tables which will be created through EF Core Code First.

DataAnnotations is a group of classes, attributes, methods. DataAnnotations are used to decorate classes and properties to enforce pre-defined validation rules. It can be used in ASP.NET MVC, Web Forms, Web API applications. Classes related to DataAnnotations belong to System.ComponentModel.DataAnnotations and System.ComponentModel.DataAnnotations.Schema namespace.

Below are some important DataAnnotations attributes

  1. Table

    Table attributes can be applied to a class to specify table names in the database. By default, EF Core will use property name used with DBSet to create a table. If your DBSet property defined as below, it creates a table with name Categories.
    public DbSet Categories { get; set; }
        

    You can change the name of a table by applying Table attribute to the Category entity class. You can use the Schema property to specify the table schema.

    using System.ComponentModel.DataAnnotations.Schema;
    
    [Table("ProductCategory", Schema="Admin")]
    public class Category
    {
           
    }
    
  2. Column

    The column attribute can be applied to entity properties to define the column name, data type, order in database table.

    Column attribute overrides default conventions.

    public class Category
    {
        public Category()
        {
    
        }
    
        [Column("CategoryID", Order = 0, TypeName="int")]
        public int CategoryID { get; set; }
            
        [Column("CategoryName", Order = 1, TypeName="varchar2")]
        public string CategoryName { get; set; }
            
        [Column("CategoryName", Order = 2, TypeName="varchar2")]
        public string Description { get; set; }        
    }
                
  3. Key

    Key attribute specify PrimaryKey of the table. As per default convention Entity Framework will create PrimaryKey for the column whose name is Id or <Entity Name>Id.

    You can use Key attributes to multiple columns to make CompositeKey with column Order specified.

    public class Product
    {
        public Product()
        {
    
        }
    
        [Column("ProductId", Order = 0, TypeName="int")]        
        public int ProductID { get; set; }
            
        [Column("CategoryID", Order = 1, TypeName="int")]
        public int CategoryID { get; set; }        
    }
    
  4. NotMapped

    NotMapped attribute can be used for properties which are not required to be created in the database. These columns are usually calculated columns or dependent on some other column.
    public class Product
    {
        public Product()
        {
                
        }
                    
        public int ProductID { get; set; }
            
        [NotMapped]
        public int ProductDiscountValue { get; set; }    
    }
                
  5. ForeignKey

    The attribute is used to configure referential integrity or foreign key relationship between two tables.

    By default Entity Framework makes a property as a foreign key when it is matching with Primary Key of the related entity.

    You can implement the foreign Key attribute as.

    public class Category
    {        
        [Key]
        public int CategoryID { get; set; }
                    
        public string CategoryName { get; set; }
            
        public string Description { get; set; }   
    
        public ICollection<Product> Products { get; set; }     
    }
    
    public class Product
    {       
        [Key]    
        public int ProductID { get; set; }
            
        [ForeignKey("Category")]     
        public int CategoryID { get; set; }
            
        public virtual Category Category { get; set; }    
    }
       
        
  6. Index

    Index Data Annotation attribute creates cluster or non cluster index on the column. You can configure index name, cluster and unique index as shown in below code snippet.

        
    public class Product
    {       
        [Key]    
        public int ProductID { get; set; }
            
        [Index("Idx_CategoryID", IsClustered=false, IsUnique=false )]     
        public int CategoryID { get; set; }
    }    
    
  7. Required

    The required attribute is used to create columns with NOT NULL constraints. If the application tryies to update data with NULL value on these columns, it throws Microsoft.EntityFrameworkCore.DbUpdateException.

    public class Product
    {
        public Product()
        {
    
        }
            
        [key]
        public int ProductID { get; set; }
            
        [Required]        
        public string ProductName { get; set; }
    
        [Required]
        public int CategoryID { get; set; }
    
        [Required]
        public int QuantityPerUnit { get; set; }
    
        public decimal UnitPrice { get; set; }
    }
    
  8. MaxLength

    MaxLength attribute is used to specify the maximum size of the string or byte[] type.

    Below code, snippet shows how to use the MaxLength Data Annotation attribute. Maximum size for CategoryName column has set as 50 characters and the Description column has set as 250 characters.

    public class Category
    {
        public Category()
        {
    
        }
        [Key]    
        public int CategoryID { get; set; }
            
        [MaxLength(50)]
        public string CategoryName { get; set; }
               
        [MaxLength(250)] 
        public string Description { get; set; }
    
        public virtual List<Products> Products { get; set; }
    }
    

    EF Core will throw Microsoft.EntityFrameworkCore.DbUpdateException if the application tries to use the length more than value of the MaxLength attribute.

  9. StringLength

    StringLength Data Annotations attribute can be used only for string properties of the entity class. When you set the StringLength attribute, Entity Framework will set the column's maximum allowed the size in table.

    StringLength and MaxLength attributes do the somewhat same thing with some differences as listed below:

    • StringLength can be applied only with string data type whereas MaxLength can be used with string and byte[].
    • StringLength can be used to specify Maximum as well as Minimum size of value whereas MaxLength is used only for Maximum size.
    • Very important StringLength validates value of property at client side as well as server side whereas MaxLength validates value only at the server side.

    You can use StringLength as shown in below code.

    public class Category
    {
    public Category()
    {
    
    }
    [Key]    
    public int CategoryID { get; set; }
            
    [StringLength(50)]
    public string CategoryName { get; set; }
               
    [StringLength(250, MinimumLength = 5)] 
    public string Description { get; set; }
    
    public virtual List<Products> Products { get; set; }
    }
    

    EF Core will throw Microsoft.EntityFrameworkCore.DbUpdateException if the application tries to use the length more than value of StringLength attribute.

  10. Timestamp

    Timestamp Data Annotations attribute can be used only with byte array type properties. Entity Framework will create a table with the timestamp and use it automatically for concurrency check while executing update statements.

    TimeStamp can be applied only for one property in an entity class. It guarantees a row with a unique value for TimeStamp type property in the database.

    Implementation of TimeStamp attribute

    public class Product
    {
        public Product()
        {
    
        }
        [Key]    
        public int ProductID { get; set; }
            
        [Timestamp]
        public byte[] ProductVersion {get; set;}
    }
    
  11. ConcurrencyCheck

    ConcurrencyCheck Data Annotation gives you a very good way to handle conflicts that can occur when multiple users try to update / delete same data.

    This attribute can be used with multiple properties in entity class and with any data type whereas TimeStamp can be applied with only one property and only with Byte[] data type.

    public class Product
    {
        public Product()
        {
    
        }
            
        [key]
        public int ProductID { get; set; }
            
        [ConcurrencyCheck]        
        public string ProductName { get; set; }                    
            
    }
    

    In above example for property ProductName attribute ConcurrencyCheck is used. Entity framework will use ProductName column to handle concurrency.

    Here is the real time scenario of Concurrency. User1 and User2 are reading details about the product having ProductName as "Product 1".

    User1 changes ProductName from "Product 1" to "Product 2", then User2 tries to change ProductName as "Product 3" however User2 did not read or do not have the latest committed changes Entity Framework throws DbConcurrencyException.

    In this case, Entity Framework will add additional WHERE Clause for column ProductName. So when User2 tries to update the same product it's query becomes as shown below which will return zero records because User1 already committed ProductName as "Product 2".

    UPDATE Products
    SET ProductName = 'Product 3'
    WHERE ProductID = 3 and ProductName = 'Product 1' 
    

Speak your mind
Please login to post your comment!