Entity Framework Core Fluent API is used to build model based on entity classes. We can also override the default Conventions of Entity Framework Core using Fluent API when targetting the database schema as Fluent API has higher precedence than conventions and data annotations.
Entity Framework Core Fluent API offers the following features.
Page Contents
Suppose we have am entity class named Country as shown below.
public class Country
{
public int PId { get; set; }
public string Name { get; set; }
public DateTime AddedOn { get; set; }
}
We write Entity Framework Core Fluent API Configurations inside the OnModelCreating() method of the Database Context (DbContext) of the app.
public class CountryContext : DbContext
{
public CompanyContext(DbContextOptions<CompanyContext> options) : base(options)
{
}
public DbSet<Country> Country { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//Write Fluent API configurations here
//Entity Configuration
modelBuilder.Entity<Country>().HasKey(s => s.PId);
//Property Configurations
modelBuilder.Entity<Country>(entity =>
{
entity.Property(e => e.Name)
.HasColumnName("CountryName")
.HasDefaultValue("USA")
.IsRequired();
entity.Property(e => e.AddedOn)
.HasColumnType("date")
.HasDefaultValueSql("(getdate())");
});
modelBuilder.Entity<Country>().Ignore(e => e.population);
}
}
In the above code the Country entity is configured in the following manner:
On doing the Entity Framework Core Migrations the Country table will be created on the database which is shown in the below image.
With Fluent APIs database Views can be mapped to entities. We have to first create a view on the database manually and then with EF Core we need to map it with an entity. For example a below given view shows data from 2 tables – Employee and Department. This is done by applying Inner Join between the 2 tables.
CREATE VIEW EmpDepView AS
SELECT e.Name, e.Designation, d.Name as "DName" FROM Employee e Inner Join Department d
On e.DepartmentId=d.Id
Next, in our app we create an Entity class as shown below.
[NotMapped]
[Keyless]
public class EmpDep
{
public string Name { get; set; }
public string Designation { get; set; }
public string DName { get; set; }
}
We don’t want any database table to be mapped to this entity so we applied [NotMapped] attribute. The [Keyless] attribute is applied because the View doesn’t contain a key value.
Next, on the DbContext we define this entity as given below.
public DbSet<EmpDep> EmpDep { get; set; }
Then finally inside the OnModelCreating() method we have to map the entity class with the view. We do this through the ToView() method as shown below.
modelBuilder.Entity<EmpDep>().ToView("EmpDepView");
We can now read the View’s data on the controller as:
public class HomeController : Controller
{
private CompanyContext context;
public HomeController(CompanyContext cc)
{
context = cc;
}
public IActionResult EmpDepView()
{
var empDep = context.EmpDep;
return View();
}
}
It is also possible to map an entity type to a table-valued function (TVF) instead of a table in the database. First create the following table-valued function in the database. The function gives departments that have more than one employees.
CREATE FUNCTION DepartmentWithMultipleEmployee()
RETURNS TABLE
AS
RETURN
(
SELECT d.Name, COUNT(e.Id) AS Count
FROM Department AS d
JOIN Employee AS e ON d.Id = e.DepartmentId
GROUP BY d.Id, d.Name
HAVING COUNT(e.Id) > 1
)
In our app we add the following class.
[NotMapped]
public class DepartmentWithMultipleEmployee
{
public string Name { get; set; }
public int Count { get; set; }
}
Next on the DbContext we define the entity.
public DbSet<DepartmentWithMultipleEmployee> DepartmentWithMultipleEmployee { get; set; }
Then with ToFunction() we map this entity class with the table-valued function (TVF). The code is given below.
modelBuilder.Entity<DepartmentWithMultipleEmployee>().HasNoKey().ToFunction("DepartmentWithMultipleEmployee");
Note that in the example, the entity is keyless, but it doesn’t have to be.
Entity Framework Core allows to generate columns values either in default manner or in computed values. For example in the below code we have applied default value as 2 for the “Rating” column of the City table. This means whenever a new row is inserted to the City table and we do not supply value for the “Rating” column then value of 2, which is the default value, for the Rating column will be inserted.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<City>()
.Property(b => b.Rating)
.HasDefaultValue(2);
}
In the below example we set the default value for the “Created” column to be generated with the SQL getdate() method. This return the current database system date and time.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<City>()
.Property(b => b.Created)
.HasDefaultValueSql("getdate()");
}
EF Core can also computes column values in the database. In the below example the value of DisplayName column is computed every time it is fetched from the database. Note that this will not store the value on the Column in the database.
modelBuilder.Entity<Employee>()
.Property(p => p.DisplayName)
.HasComputedColumnSql("[LastName] + ', ' + [FirstName]");
We can also let the computes column values to be stored on the table’s Column by using stored: true. Check below example:
modelBuilder.Entity<Employee>()
.Property(p => p.DisplayName)
.HasComputedColumnSql("[LastName] + ', ' + [FirstName]", stored: true);
EF Core can also add contraints which are some conditions that must hold for all rows in a table. Attempts to insert or modify data that violates the constraints will fail.
In the below code we added a constraint Check_Salary that ensures that the Salary column in the database is always more than Tax column for every row of the table.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Employee>()
.ToTable(b => b.HasCheckConstraint("Check_Salary", "[Salary] > [Tax]"));
}
In the below table we have listed down some of the most common methods of Entity Framework Core Fluent API.
Configuration Type | Fluent API Methods | Usage |
---|---|---|
Model Configuration | HasDbFunction() | Configures a database function |
Model Configuration | HasDefaultSchema() | Specifies the database schema |
Model Configuration | HasSequence() | Configures the database sequence |
Entity Configuration | HasIndex() | Configures a property as Index |
Entity Configuration | ToTable() | Configures the name of the database table that the entity corresponds to. |
Entity Configuration | HasKey() | Configures a property as Primary Key |
Entity Configuration | HasNoKey() | Tells that the entity cannot have a key. Such entities are never tracked for changes and therefore are never inserted, updated or deleted on the database. |
Entity Configuration | HasOne() | Configures the One part of the relationship, for one-to-one or one-to-many relationships. |
Entity Configuration | HasMany() | Configure the many side of a one-to-many relationship. |
Entity Configuration | WithOne() | Configures the One part of the relationship, for one-to-one or one-to-many relationships. |
Entity Configuration | WithMany() | Configure the many side of a one-to-many relationship.. |
Entity Configuration | HasForeignKey() | Configures the property as the foreign key |
Entity Configuration | Ignore() | Specifies the property should not be mapped to a database table |
Entity Configuration | HasConstraintName() | Specifies name of the foreign key constraint for a relationship |
Property Configuration | HasColumnName() | Specifies the column name in the database for the property |
Property Configuration | HasColumnType() | Specifies the data type of the column in the database for the property |
Property Configuration | HasDefaultValue() | Specifies the default value of the column in the database for the property |
Property Configuration | HasDefaultValueSql() | Specifies the default value expression of the column in the database for the property. Eg getdate() |
Property Configuration | HasMaxLength() | Specifies the maximum length of the column in the database for the property |
Property Configuration | IsRequired() | Specifies the database column should be not null |
Property Configuration | IsUnicode() | Specifies that the column should contain Unicode characters |
Property Configuration | ValueGeneratedOnAdd() | Configures that the property has a generated value when saving a new entity |
The Has/With pattern is widely used when configuring relationships with the Fluent API. The Has & With methods must be combined to configure a valid relationship.
There are 2 variants of Has method, these are:
Similarly, there are 2 variants of With method, these are:
The HasOne method along with the WithOne method is used to create Reference Navigation Properties. Similarly, the HasMany method along with the WithMany method is used to create Collection Navigation Properties.
You will learn the usage of these methods in the below 3 tutorials.