Entity Framework: Code First Data Annotations

Introduction


The Entity Framework Code First approach allows us to use our POCO (domain) classes as a model and Entity Framework can use the classes to query data, change tracking, and other update functions.

Entity Framework Code First provides a set of data annotation attributes that can be applied on domain classes or the properties of domain classes.

Normally we can divide these data annotations into the following two categories:
  • Database Schema related Attributes

    • Table
    • Column
    • Key
    • Timestamp
    • ConcurrencyCheck
    • ForeignKey
    • InverseProperty
    • Index
    • DatabaseGenerated
    • ComplexType
    • NotMapped

  • Validation Attributes

    • Required
    • MinLength
    • MaxLength
    • StringLength

Table Attribute


Table attributes specify the name of the table and define the schema. This means that this attribute can be used when the entity name and table name in the database are different.
  1. Table("Department",  Schema = "dbo")]  
  2. public class DepartmentMaster  
  3. {  
  4.   

In the preceding example, my entity name is “DepartmentMaster” and my database table name is “Department”.

Column Attribute


The same as the table attribute, this attribute specifies the name of the column and the data type of the column. This means that this attribute is very useful when the entity's property name and the table's column name are different.

The following are some important properties of the Column Attribute:
  • Order: Order in which a column appears in the table
  • TypeName: Name database type of the column
Example:
  1. [Table("Employee", Schema = "dbo")]  
  2. public class Employee  
  3. {  
  4.     [Column("ID", Order = 1)]  
  5.     public int EmployeeId { getset; }  
  6.     [Column("Name", Order = 2, TypeName = "Varchar(100)")]  
  7.     public string EmployeeName { getset; }  

Key Attribute


Entity Framework believes that every entity has a primary key and that key is used for tracking the entities. The Key Attribute specifies the property/column that is the part of the primary key of the entity and it applies only to scalar properties.

Example
  1. [Table("Department"Schema = "dbo")]  
  2.     public class DepartmentMaster  
  3.     {  
  4.         [Key]  
  5.         public int DepartmentId { get; set; }  

Composite keys


An Entity Framework Code First model also supports a composite primary key (a composite primary key has more than one property).

Example
  1. [Table(“Department”, Schema = "dbo")]  
  2.     public class DepartmentMaster  
  3.     {  
  4.         [Key]  
  5.         public int DepartmentId { getset; }  
  6.         [Key]  
  7.         public int CompanyId { getset; }  

Timestamp Attribute


The Timestamp attribute specifies the byte array (byte []) property/column that has a concurrency mode of "Fixed" in the model and it should be a Timestamp column in the stored model (database).

Example
  1. [Timestamp]  
  2. public Byte[] TimeStamp { getset; } 

ConcurrencyCheck Attribute


The ConcurrencyCheck Attribute is used to specify a property/column that has a concurrency mode of “fixed” in the EDM model. This attribute can be used with scalar properties only. A fixed concurrency mode means that this property is being a part of the concurrency check during save operations.

Example
  1. [ConcurrencyCheck]  
  2. public string Name { getset; } 

ForeignKey Attribute


This attribute specifies the foreign key for the Navigation property.

Example
  1. [Table("Employee", Schema = "dbo")]  
  2. public class Employee  
  3. {  
  4.     [Column("ID", Order = 1)]  
  5.     public int EmployeeId { getset; }  
  6.     [Column("Name", Order = 2, TypeName = "Varchar(100)")]  
  7.     public string EmployeeName { getset; }  
  8.     [ForeignKey("Department ")]  
  9.     public int DepartmentId { getset; }  
  10.     [ForeignKey("DepartmentId")]  
  11.     public DepartmentMaster Department { getset; }  

InverseProperty Attribute


A relationship in the Entity Framework always has two ends, a navigation property on each side and an Entity Framework that maps them together automatically by convention. If there are multiple relationships between the two entities, Entity Framework cannot handle the relationships. This is because Entity Framework doesn't know which navigation property map with which properties on another side. For example, Employee is bound with two departments one is the primary and the other is the secondary. In this scenario, Entity Framework does not know which navigation properties on the opposite side should be returned.

Using an InverseProperty attribute, we can specify which navigation property should be returned.

Example
  1. [Table("Department", Schema = "dbo")]  
  2. public class DepartmentMaster  
  3. {  
  4.     [Key]  
  5.     public int DepartmentId { getset; }  
  6.     [Required]  
  7.     public string Code { getset; }  
  8.     [MinLength(5)]  
  9.     [MaxLength(100)]  
  10.     [Index("IX_Name_DepartmentMaster", IsClustered = false, Order = 2)]  
  11.     public string Name { getset; }  
  12.     public ICollection<Employee> PrimaryEmployees { getset; }  
  13.     public ICollection<Employee> SecondaryEmployees { getset; }  
  14. }  
  15.   
  16. [Table("Employee", Schema = "dbo")]  
  17. public class Employee  
  18. {  
  19.     [Column("ID", Order = 1)]  
  20.     public int EmployeeId { getset; }  
  21.     [Column("Name", Order = 2, TypeName = "Varchar(100)")]  
  22.     public string EmployeeName { getset; }  
  23.   
  24.     [InverseProperty("PrimaryEmployees")]  
  25.     public DepartmentMaster PrimaryDepartment { getset; }  
  26.   
  27.     [InverseProperty("SecondaryEmployees")]  
  28.     public DepartmentMaster SecondaryDepartment { getset; }  

Index Attribute


An index data annotation attribute is introduced with Entity Framework 6.1. The property of the model can be marked with an attribute and it should participate in a store index. The Index attribute allows us to create an index on one or more columns and we can specify the name of the index.

Example
  1. [Index("IX_Name_DepartmentMaster", IsClustered = false)]  
  2. public string Name { getset; } 

DatabaseGenerated Attribute


The DatabaseGenerated attribute specifies that the property of the model will be used for either identity or Computed column of the database table so that this property will be read-only and the Entity Framework does not try to update this field into the database table. The Entity Framework will update this field from the database after the insert or update.

Example
  1. [DatabaseGenerated(DatabaseGeneratedOption.Computed)]  
  2. public DateTime DateCreated { getset; } 
 

ComplexType Attribute


The Complex types are the non-scalar properties of an entity that enable scalar properties to be organized within the entities. A Complex type may have scalar properties or other complex type properties. Complex types do not have a key and Identity so that Entity Framework cannot manage these objects apart from the parent entity. A Complex type entity can be also used for a Stored Procedure result.

Example
  1. [ComplexType]  
  2. public class UserInfo  
  3. {  
  4.     public DateTime CreatedDate { getset; }  
  5.     public string CreatedBy { getset; }  
  6. }  
  7.   
  8. [Table("Department", Schema = "dbo")]  
  9. public class DepartmentMaster  
  10. {  
  11.     [Key]  
  12.     public int DepartmentId { getset; }  
  13. ….  
  14. ….  
  15.    public UserInfo User {get;set;}  

NotMapped Attribute


In the Code First model approach, every property of the model is represented as a table's column in the database. This is not always the case, we might require some property in a model or entity that is not present in the database table. For example, my Department entity has a property called DepartmentCodeName, this property returns a combination of code and name separated by a colon(:). This property can be created dynamically and there is no need to store it in the database. We can mark this property with NotMapped annotation. In short, if we decorate any property or class with this attribute then they should be excluded from database mapping.

Example
  1. public class DepartmentMaster  
  2. {  
  3.         [NotMapped]  
  4.         public string DepartmentCodeName  
  5.         {  
  6.             get  
  7.             {  
  8.                 return Code + ":" + Name;  
  9.             }  
  10.         }  
  11. …  
  12. ….  
  13. }  
  14.   
  15. [NotMapped]  
  16. public class InternalClass  
  17. {  
  18.     public int Id { getset; }  
  19.     public string Name { getset; }  

Required Attribute


The Required attribute tells the Entity Framework that this property must have a value and this attribute will force the Entity Framework to ensure that this has data in it. This attribute will also participate in database creation (by marking this column as “not nullable”).

Example
  1. [Required]  
  2. public string Code { getset; } 
 

MinLength Attribute


This attribute is used to validate a property, whether the property has a minimum length of string.

Example
  1. [MinLength(5)]  
  2. public string Name { getset; } 

MaxLength Attribute


The MaxLength attribute allows us to specify additional property validations to set the maximum length of the string. This attribute will also participate in database creation (by setting the length of the property).

Example
  1. [MinLength(5)]  
  2. [MaxLength(100)]  
  3. public string Name { getset; } 

StringLength Attribute


StringLength is used to specify the maximum length of the string. This attribute is applied only to string type properties. We can also specify a minimum length of characters that are allowed in the data field. This attribute will also participate in database creation (by setting the length of the property).

Example
  1. [StringLength(100, MinimumLength = 5)]  
  2. public string Name { getset; } 
 

Summary


Generally, data annotations are used for client-side and server-side validation. This article describes how data annotation can help us to configure our classes in the Code First model.