Entity Framework

Code First DataAnnotations

Remarks#

Entity Framework Code-First provides a set of DataAnnotation attributes, which you can apply to your domain classes and properties. DataAnnotation attributes override default Code-First conventions.

  1. System.ComponentModel.DataAnnotations includes attributes that impacts on nullability or size of the column.
  2. System.ComponentModel.DataAnnotations.Schema namespace includes attributes that impacts the schema of the database.

Note: DataAnnotations only give you a subset of configuration options. Fluent API provides a full set of configuration options available in Code-First.

[Key] attribute

Key is a field in a table which uniquely identifies each row/record in a database table.

Use this attribute to override the default Code-First convention. If applied to a property, it will be used as the primary key column for this class.

using System.ComponentModel.DataAnnotations;

public class Person
{
    [Key]
    public int PersonKey { get; set; }        // <- will be used as primary key
     
    public string PersonName { get; set; }    
}

If a composite primary key is required, the [Key] attribute can also be added to multiple properties. The order of the columns within the composite key must be provided in the form [Key, Column(Order = x)].

using System.ComponentModel.DataAnnotations;

public class Person
{
    [Key, Column(Order = 0)]
    public int PersonKey1 { get; set; }    // <- will be used as part of the primary key

    [Key, Column(Order = 1)]
    public int PersonKey2 { get; set; }    // <- will be used as part of the primary key
     
    public string PersonName { get; set; }    
}

Without the [Key] attribute, EntityFramework will fall back to the default convention which is to use the property of the class as a primary key that is named “Id” or “{ClassName}Id”.

public class Person
{
    public int PersonID { get; set; }        // <- will be used as primary key
     
    public string PersonName { get; set; }    
}

[Required] attribute

When applied to a property of a domain class, the database will create a NOT NULL column.

using System.ComponentModel.DataAnnotations;

public class Person
{
    public int PersonID { get; set; }
    
    [Required]
    public string PersonName { get; set; }    
}

The resulting column with the NOT NULL constraint:

The resulting column in a SQL Server

Note: It can also be used with asp.net-mvc as a validation attribute.

[MaxLength] and [MinLength] attributes

[MaxLength(int)] attribute can be applied to a string or array type property of a domain class. Entity Framework will set the size of a column to the specified value.

using System.ComponentModel.DataAnnotations;

public class Person
{
    public int PersonID { get; set; }
    
    [MinLength(3), MaxLength(100)]
    public string PersonName { get; set; }    
}

The resulting column with the specified column length:

enter image description here

[MinLength(int)] attribute is a validation attribute, it does not affect the database structure. If we try to insert/update a Person with PersonName with length less than 3 characters, this commit will fail. We’ll get a DbUpdateConcurrencyException that we’ll need to handle.

using (var db = new ApplicationDbContext())
{                    
     db.Staff.Add(new Person() { PersonName = "ng" });
     try
     {
          db.SaveChanges();
     }
     catch (DbEntityValidationException ex)
     {
          //ErrorMessage = "The field PersonName must be a string or array type with a minimum length of '3'."
     }
}

Both [MaxLength] and [MinLength] attributes can also be used with asp.net-mvc as a validation attribute.

[Range(min,max)] attribute

Specifies a numeric minimum and maximum range for a property

using System.ComponentModel.DataAnnotations;   

public partial class Enrollment
{
    public int EnrollmentID { get; set; }
   
    [Range(0, 4)]
    public Nullable<decimal> Grade { get; set; }
}

If we try to insert/update a Grade with value out of range, this commit will fail. We’ll get a DbUpdateConcurrencyException that we’ll need to handle.

using (var db = new ApplicationDbContext())
{
    db.Enrollments.Add(new Enrollment() { Grade = 1000 });

    try
    {
        db.SaveChanges();
    }
    catch (DbEntityValidationException ex)
    {
        // Validation failed for one or more entities
    }
}

It can also be used with asp.net-mvc as a validation attribute.

Result:

enter image description here

[DatabaseGenerated] attribute

Specifies how the database generates values for the property. There are three possible values:

  1. None specifies that the values are not generated by the database.
  2. Identity specifies that the column is an identity column, which is typically used for integer primary keys.
  3. Computed specifies that the database generates the value for the column.

If the value is anything other than None, Entity Framework will not commit changes made to the property back to the database.

By default (based on the StoreGeneratedIdentityKeyConvention) an integer key property will be treated as an identity column. To override this convention and force it to be treated as a non-identity column you can use the DatabaseGenerated attribute with a value of None.

using System.ComponentModel.DataAnnotations.Schema;

public class Foo
{
    [Key]
    public int Id { get; set; } // identity (auto-increment) column
}

public class Bar
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; } // non-identity column
}

The following SQL creates a table with a computed column:

CREATE TABLE [Person] (
    Name varchar(100) PRIMARY KEY,
    DateOfBirth Date NOT NULL,
    Age AS DATEDIFF(year, DateOfBirth, GETDATE())
)
GO

To create an entity for representing the records in the above table, you would need to use the DatabaseGenerated attribute with a value of Computed.

[Table("Person")]
public class Person
{
    [Key, StringLength(100)]
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public int Age { get; set; }
}

[NotMapped] attribute

By Code-First convention, Entity Framework creates a column for every public property that is of a supported data type and has both a getter and a setter. [NotMapped] annotation must be applied to any properties that we do NOT want a column in a database table for.

An example of a property that we might not want to store in the database is a student’s full name based on their first and last name. That can be calculated on the fly and there is no need to store it in the database.

public string FullName => string.Format("{0} {1}", FirstName, LastName);

The “FullName” property has only a getter and no setter, so by default, Entity Framework will NOT create a column for it.

Another example of a property that we might not want to store in the database is a student’s “AverageGrade”. We do not want to get the AverageGrade on-demand; instead we might have a routine elsewhere that calculates it.

[NotMapped]
public float AverageGrade { set; get; }

The “AverageGrade” must be marked [NotMapped] annotation, else Entity Framework will create a column for it.

using System.ComponentModel.DataAnnotations.Schema;

public class Student
{
    public int Id { set; get; }

    public string FirstName { set; get; }

    public string LastName { set; get; }

    public string FullName => string.Format("{0} {1}", FirstName, LastName);

    [NotMapped]
    public float AverageGrade { set; get; }
}

For the above Entity we will see inside DbMigration.cs

CreateTable(
    "dbo.Students",
     c => new
         {
             Id = c.Int(nullable: false, identity: true),
             FirstName = c.String(),
             LastName = c.String(),
         })
     .PrimaryKey(t => t.Id);

and in SQL Server Management Studio

enter image description here

[Table] attribute

[Table("People")]
public class Person
{
    public int PersonID { get; set; }
    public string PersonName { get; set; }    
}

Tells Entity Framework to use a specific table name instead of generating one (i.e. Person or Persons)

We can also specify a schema for the table using [Table] attribute

[Table("People", Schema = "domain")]

[Column] attribute

public class Person
{
    public int PersonID { get; set; }
    
    [Column("NameOfPerson")]
    public string PersonName { get; set; }    
}

Tells Entity Framework to use a specific column name instead using the name of the property. You can also specify the database data type and the order of the column in table:

[Column("NameOfPerson", TypeName = "varchar", Order = 1)]
public string PersonName { get; set; }    

[Index] attribute

public class Person
{
    public int PersonID { get; set; }
    public string PersonName { get; set; }

    [Index]
    public int Age { get; set; }
}

Creates a database index for a column or set of columns.

[Index("IX_Person_Age")]
public int Age { get; set; }

This creates an index with a specific name.

[Index(IsUnique = true)]
public int Age { get; set; }

This creates a unique index.

[Index("IX_Person_NameAndAge", 1)]
public int Age { get; set; }

[Index("IX_Person_NameAndAge", 2)]
public string PersonName { get; set; }

This creates a composite index using 2 columns. To do this you must specify the same index name and provide a column order.

Note: The Index attribute was introduced in Entity Framework 6.1. If you are using an earlier version the information in this section does not apply.

[ForeignKey(string)] attribute

Specifies custom foreign key name if a foreign key not following EF’s convention is desired.

public class Person 
{
    public int IdAddress { get; set; }

    [ForeignKey(nameof(IdAddress))]
    public virtual Address HomeAddress { get; set; }
}

This can also be used when you have multiple relationships to the same entity type.

using System.ComponentModel.DataAnnotations.Schema;

public class Customer
{
    ...

    public int MailingAddressID { get; set; }
    public int BillingAddressID { get; set; }

    [ForeignKey("MailingAddressID")]
    public virtual Address MailingAddress { get; set; }
    [ForeignKey("BillingAddressID")]
    public virtual Address BillingAddress { get; set; }
}

Without the ForeignKey attributes, EF might get them mixed up and use the value of BillingAddressID when fetching the MailingAddress, or it might just come up with a different name for the column based on its own naming conventions (like Address_MailingAddress_Id) and try to use that instead (which would result in an error if you’re using this with an existing database).

[StringLength(int)] attribute

using System.ComponentModel.DataAnnotations;

public class Post
{
    public int Id { get; set; }
    
    [StringLength(100)]
    public string Title { get; set;}

    [StringLength(300)]
    public string Abstract { get; set; }
    
    public string Description { get; set; }
}

Defines a maximum length for a string field.

enter image description here

Note: It can also be used with asp.net-mvc as a validation attribute.

[Timestamp] attribute

[TimeStamp] attribute can be applied to only one byte array property in a given Entity class. Entity Framework will create a non-nullable timestamp column in the database table for that property. Entity Framework will automatically use this TimeStamp column in concurrency check.

using System.ComponentModel.DataAnnotations.Schema;

public class Student
{
    public int Id { set; get; }

    public string FirstName { set; get; }

    public string LastName { set; get; }    

    [Timestamp]
    public byte[] RowVersion { get; set; }
}

enter image description here

[ConcurrencyCheck] Attribute

This attribute is applied to the class property. You can use ConcurrencyCheck attribute when you want to use existing columns for concurrency check and not a separate timestamp column for concurrency.

using System.ComponentModel.DataAnnotations;

public class Author
{
    public int AuthorId { get; set; }
     
    [ConcurrencyCheck]
    public string AuthorName { get; set; }
}

From above example, ConcurrencyCheck attribute is applied to AuthorName property of the Author class. So, Code-First will include AuthorName column in update command (where clause) to check for optimistic concurrency.

[InverseProperty(string)] attribute

using System.ComponentModel.DataAnnotations.Schema;

public class Department  
{  
    ...

    public virtual ICollection<Employee> PrimaryEmployees { get; set; }  
    public virtual ICollection<Employee> SecondaryEmployees { get; set; }  
}  
  
public class Employee  
{  
    ...

    [InverseProperty("PrimaryEmployees")]  
    public virtual Department PrimaryDepartment { get; set; }  
  
    [InverseProperty("SecondaryEmployees")]  
    public virtual Department SecondaryDepartment { get; set; }  
}  

InverseProperty can be used to identify two way relationships when multiple two way relationships exist between two entities.

It tells Entity Framework which navigation properties it should match with properties on the other side.

Entity Framework doesn’t know which navigation property map with which properties on the other side when multiple bidirectional relationships exist between two entities.

It needs the name of the corresponding navigation property in the related class as its parameter.

This can also be used for entities that have a relationship to other entities of the same type, forming a recursive relationship.

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

public class TreeNode
{
    [Key]
    public int ID { get; set; }
    public int ParentID { get; set; }

    ...

    [ForeignKey("ParentID")]
    public TreeNode ParentNode { get; set; }
    [InverseProperty("ParentNode")]
    public virtual ICollection<TreeNode> ChildNodes { get; set; }
}

Note also the use of the ForeignKey attribute to specify the column that is used for the foreign key on the table. In the first example, the two properties on the Employee class could have had the ForeignKey attribute applied to define the column names.

[ComplexType] attribute

using System.ComponentModel.DataAnnotations.Schema;

[ComplexType] 
public class BlogDetails 
{ 
    public DateTime? DateCreated { get; set; } 
 
    [MaxLength(250)] 
    public string Description { get; set; } 
}

public class Blog
{
    ...

    public BlogDetails BlogDetail { get; set; }
}

Mark the class as complex type in Entity Framework.

Complex Types (Or Value Objects In Domain Driven Design) cannot be tracked on their own but they are tracked as part of an entity. This is why BlogDetails in the example does not have a key property.

Complex Type being tracked

They can be useful when describing domain entities across multiple classes and layering those classes into a complete entity.


This modified text is an extract of the original Stack Overflow Documentation created by the contributors and released under CC BY-SA 3.0 This website is not affiliated with Stack Overflow