EF Core Conventions or the default rules that you follow while creating the entity model. The EF Core uses these to infer and to configure the Database. It uses the information available in the POCO Classes to determine and infer the schema of the database that these classes are mapped to. For example, the table name, Column Name, Data Type, Primary keys are inferred from the Class name, property name & Property type by convention to build the database.
Table of Contents
Conventions in EF Core
The EF Core makes certain assumptions based on how your code for domain model is written before creating the tables in the database. These are called Entity Framework core conventions or Entity Framework core naming conventions.
Preparing the Example Project
Create a new console app. We covered this in the tutorial EF Core example application. Name the project as EFCoreConventions. Install the following packages via NuGet Package Manager.
1 2 3 4 | install-package Microsoft.EntityFrameworkcore.SqlServer install-package Microsoft.EntityFrameworkcore.Tools |
Create Models folder
Under the Models folder create the Context class EFContext.cs as shown here
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using Microsoft.EntityFrameworkCore; namespace EFCoreConventions.Models { public class EFContext : DbContext { private const string connectionString = "Server=(localdb)\\mssqllocaldb;Database=EFCore;Trusted_Connection=True;"; protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(connectionString); } } } |
Under the Models folder create the models.cs, which is going to hold our domain models
Now, we are ready
Type Discovery
Every entity model must declare a DbSet property in the DbContext. EF Core builds the database by inspecting the DbSet Property using the reflection. It then creates tables for these types. It also includes the referenced types, which do not expose DbSet Property. Inherited types are also created if the base class exposed the DbSet Property
Open the Models.cs and add the customer class as shown below
1 2 3 4 5 6 7 8 | public class Customer { public int CustomerID { get; set; } public string CustomerName { get; set; } public string PhoneNo { get; set; } } |
Open the Context class and add the DbSet Property for the customer Model
1 2 3 | public DbSet<Customer> Customers { get; set; } |
Now, open the Package Console Manager and run the command
1 2 3 4 | add-migration "V1" update-database |
Open the database and you should be able to see the following
The Customer table is created. Note that the table name is taken from the DbSet Property as Customers
1 2 3 | public DbSet<Customer> Customers { get; set; } |
Next, we will add another model CustmerAddress
1 2 3 4 5 6 7 8 9 10 11 | public class CustomerAddress { public int id {get; set;} public int CustomerID { get; set; } public string Address { get; set; } public string City { get; set; } public string State { get; set; } public Customer Customer { get; set; } } |
Do not add DbSet property for the CustomerAddress table.
You can remove the migration by running the following commands in PMC.
1 2 3 4 | update-database 0 Remove-migration |
Then, apply the migration again.
Check the Database. You will see the CustomerAddress table is created. This is because it is referenced by the Customer Table.
Entity Class Convention
As seen in the above example, the entity classes are mapped to the database. For Entity Classes to be mapped to the database, they need to follow few conventions
- The class must be declared as public
- Static classes are not allowed. The EF must be able to create an instance of the class
- The Class should not have any constructor with a parameter. The parameterless constructor is allowed
Database Schema Name Convention
The EF Core creates the database using the schema is dbo.
Table Name Convention
EF Core creates the tables using the Dbset property of the entity class names. In the above example, the table name for customer class is customers as it is defined as customers in DbSet
If The DbSet property is not defined, then the class name is taken as default as in the case of customerAddress table
Remember, the old entity framework code first conventions used to pluralize the table names by default. This is dropped in EF Core.
Column Name
Table Column names are named after the property name. The property must have a public getter. The setter can have any access mode
Data types and column length
The .NET type are mapped to corresponding SQL Datatype. For Example, the String Properties are mapped to nullable nvarchar(max). The Byte array (byte[]) becomes varbinary(max) and Booleans get mapped to the bit.
The following is the DbTypeTest model with fields for various data types.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class DbTypeTest { public int id { get; set; } public string TestString { get; set; } public Decimal TestDecimal { get; set; } public Decimal? TestDecimalNull { get; set; } public Double TestDouble { get; set; } public Double? TestDoubleNull { get; set; } public int Testint { get; set; } public int? TestIntNull { get; set; } public bool Testbool { get; set; } public bool? TestboolNull { get; set; } public DateTime TestDateTime { get; set; } public DateTime? TestDateTimeNull { get; set; } public byte TestByte { get; set; } public byte? TestByteNull { get; set; } public byte[] TestByteA { get; set; } public uint TestUnit { get; set; } public uint? TestUnitNull { get; set; } public short TestShort { get; set; } public ushort TestUShort { get; set; } public char testChar { get; set; } } |
The list of Data types and their columns mappings is listed below.
Data type | Mapped | Null ? |
---|---|---|
string | nvarchar(max) | Null |
decimal | decimal(18, 2) | Not Null |
decimal? | decimal(18, 2) | Null |
double | float | Not Null |
double? | float | Null |
int | int | Not Null |
int? | int | Null |
bool | bit | Not Null |
bool? | bit | Null |
DateTime | datetime | Not Null |
DateTime? | datetime | Null |
byte[] | varbinary(max) | Null |
byte | tinyint | Not Null |
byte? | tinyint | Null |
uint | biint | not null |
uint? | biint ? | null |
short | smallint | not null |
ushort | int | not null |
char | nvarchar(1) | not null |
Nullability of Column
The Nullability of the data type depends on the .NET
Primary Key Convention
Entity Framework Core does not allow you to create the tables without the primary key. It follows the convention to identify the candidate for the Primary Key. It searches any property with the name ID or <className>ID and uses it as Primary Key
In the above example, we have not specified the Primary key in the model definition. But the id column from customerAddress table & cusomterID from the customer table is chosen as primary key based on the convention.
If the model contains both id & <className>ID columns, then id is chosen as Primary key
If you do not specify the primary key, then the add-migration will throw an error. For Example, try to remove the id field from the customerAddress table and check. You will get the following error
“System.InvalidOperationException: The entity type ‘CustomerAddress’ requires a primary key to be defined.”
The EF core will create the identity column if the Primary Key is defined as numeric.
The guid data type in primary key is defined as uniqueidentifier.
The older Entity Framework did not support unsigned data types. The EF Core maps the uint type bigInt datatype
Foreign Key Convention
In relational databases, data is divided between related tables. The relationship between these tables defined using the foreign keys. if you are looking for an employee working in a particular department, then you need to specify the relationship between employees and department table.
One to many Relationship
An Employee belongs to a Department. A Department can have many employees. This is a One to many relationships. This relationship is captured by adding the reference to the Department table in Employee model as shown below
Let us create models to handle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class Employee { public uint EmployeeID { get; set; } public string EmployeeName { get; set; } public Department Department { get; set; } } public class Department { public int DepartmentID { get; set; } public string DepartmentName { get; set; } } |
We also need to add DbSet property so that EF Core will discover these models. You can choose to add DbSet for Employee class only as the Department is referred by Employee model, hence automatically created. But, we will add DbSet for all of our models
1 2 3 4 5 6 7 | public DbSet<Customer> Customers { get; set; } public DbSet<CustomerAddress> CustomerAddresses { get; set; } public DbSet<Employee> Employees { get; set; } public DbSet<Department> Departments { get; set; } public DbSet<DbTypeTest> DbTypeTest { get; set; } |
Now drop the database recreate the migration and update the database
Note, that we did not have DepartmentID field in the Employee Model. But EFCore infers the reference to the Department table from the code public Department Department { get; set; } as Foreign key and create the DepartmentID column in Employee table as FK
The Older versions used to create the field as “Department_DepartmentID”
The one to many relations can also be specified in another way. For Example in the following model we just removed the department property from Employee table and moved it as the collection of employees in the department table
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class Employee { public uint EmployeeID { get; set; } public string EmployeeName { get; set; } } public class Department { public int DepartmentID { get; set; } public string DepartmentName { get; set; } public ICollection<Employee> Employeees { get; set; } } |
This will result in the same database structure.
And infact you should add navigational property in both the models
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class Employee { public uint EmployeeID { get; set; } public string EmployeeName { get; set; } public Department Department { get; set; } } public class Department { public int DepartmentID { get; set; } public string DepartmentName { get; set; } public ICollection<Employee> Employeees { get; set; } } |
Many to many Relationship
Many to Many relationship is not supported yet in Entity Framework Core 2.0.
Indexes
Indexes for Foreign key is automatically created by the EF Core
Conclusion
The EF Core conventions provide a starting point for configuring the model. You can further fine-tune the model using the Data Annotations & Fluent API, which we will cover in the next tutorials
Thank you so much from Viet Nam.
Great tutorial, and now the EFCore 5 supports Many-to-Many relationship
The naming conventions suggested for EF is incorrect in this article. Specifically key fields should be TablenameId not Id as in your samples.
Thanks for the reply
You are confusing naming conventions with the EF Core conventions
If your class has any property with the name ID orID, the EF Core automatically creates a Primary Key Fields. That’s convention followed by EF Core.
You can useId as the primary key naming convention for your classes. Then you can use [Key] attribute to mark it as Primary Key (if classname is different from Tablename). That’s entirely up to you.