Entity Framework Core
Entity Framework uses EDM (Entity Data Model) for all database-related operations.
EF Database Context is a bridge between the Entity Classes (C# classes) and the database tables (SQL).
ORM (Object Relational Mapper):
- Allow the computer to generate database tables based on classes defined in the application
- Database is updated using migrations
- Entity Framework is Microsoft's ORM
- Simple for basic applications
Data Access Object (DAO) (Old way):
- Manually create tables
- Traditional method of database access
- Write your own SQL statements
- Database managers (DBA's) prefer DAOs
- Provides more visibility on finding problems
Setup
Few Packages are need to work with EF Core:
DB Provider Package: Specific to the DB that is being used:
Microsoft.EntityFrameworkCore.SqlServerif the DB is SQL Server- The other dependent packages are installed automatically such as
Relational,EntityFrameworkCore
Type of DB: Relational, NoSQL, etc.
Microsoft.EntityFrameworkCore.Relationalas SQL Server is RDBMS
Microsoft.EntityFrameworkCorepackage contains the EF Core functionality
Create the DbContext class:
cspublic class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } // Model (Entity) public DbSet<Book> Books { get; set; } }Register and configure the
DbContextclass with a DB provider to the DI system in theStartup.csfile:AddDbContextPoolhas better performance thanAddDbContext- Don't use
AddDbContextPoolif private properties are used
cspublic void ConfigureServices(IServiceCollection services) { // In-memory services.AddDbContextPool<TodoContext>(opt => opt.UseInMemoryDatabase("TodoList")); // SQLite services.AddDbContextPool<AppDbContext>(options => options.UseSqlite($"Data Source={DbPath}")); // MySQL services.AddDbContextPool<AppDbContext>(options => options.UseMySQL(Configuration.GetConnectionString("DefaultConnectionString"))); }
DbContext Class
To use DbContext class in our application, create a class that derives from the DbContext class:
- Pass configuration information to the
DbContextusingDbContextOptionsinstance - Think of it as the database
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
// Model (Entity)
public DbSet<Book> Books { get; set; }
}The
DbContextclass includes aDbSet<TEntityproperty for each entity in the modelThink of it as the table in the database
This
DbSetpropertyBookswill be used to query and save instances of the Book classThe name of this
DbSetproperty corresponds to the table name EF Core will createThe columns of this table will correspond to the properties of
Booksclass
The LINQ queries against the
DbSet<TEntity>will be translated into queries against the underlying database
Database Providers
EF Core wide verity of databases.
Sample list:
SQLite:
Microsoft.EntityFrameworkCore.SqliteSQL Server
MySQL
Maria
Oracle
PostgreSQL
In-memory: The In-Memory provider was not designed for use outside of testing environments and should never be used as such:
Microsoft.EntityFrameworkCore.InMemory
Repository Pattern
It is an abstraction of the data access layer, it hides the details of how exactly the data is saved or retrieved from the underlying data source (such as databases, flat files, web services, etc.)
- The details of how the data is stored and retrieved is in the respective repository
- It provides a single point of entry for retrieving and manipulating data, allowing the rest of the application to be decoupled from the specifics of the data storage layer
Migrations
EF Core Migrations: Provides a way to incrementally update the database schema to keep in sync with the application's data model while preserving existing data in the database.
In other words, Migration keeps the database schema and application model classes in sync
Install
Microsoft.EntityFrameworkCore.Toolsfor EF Core MigrationsAdd the connection string into
appsettings.jsonfile. For MySQL it isServer=localhost;port=3306;user=;database=;password=Run migration:
Add-Migration InitialDbMigrationIt will create a class with the name provided during migration which inherits from
MigrationIt will have methods such as
CreateTablewith the name provide in the connection stringUpdate the database:
Update-Database
Add a new migration:
bash# Help command Get-Help about_EntityFrameworkCore # string can be anything meaningful # old way: Enable migrations Add-Migration "initialSetup" # using `dotnet` CLI dotnet ef migrations add "initialSetup"- The above command create a directory called "Migrations" at the root of the project
- Inside this directory a new files are created:
XXXXX_[name of migration].cs-: The main migrations file. It contains the operations necessary to apply the migration (inUp) and to revert it (inDown).XXXXX_[name of migration].Designer.cs: The migrations metadata file. Contains information used by EF.AppDbContextModelSnapshot.cs: A snapshot of your current model. Used to determine what changed when adding the next migration.
Update the database to the last migration or to a specified migration.
bash# using `dotnet` CLI dotnet ef database update # update database to the last migration Update-Database # update database to a specific migration Update-Database [migration name or migration id] # reverts all migrations Update-Database 0
Remove-Migration
Removes the last migration (rolls back the code changes that were done for the migration):
Remove-Migration
# using `dotnet` CLI
dotnet ef migrations removeRemoving migration that has already been applied to the database will throw error
To undo the applied migrations use
Update-Databasecommand with the name of the migration previous to the one that needs to be revertedThen run
Remove-Migrationto remove the migration
Data Seeding
Data seeding is the process of populating a database with an initial set of data.
There are several ways this can be accomplished in EF Core:
Model seed data:
csprotected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 1, Url = "http://sample.com" }); }- Id needs to be provided
Manual migration customization:
csprotected override void OnModelCreating(ModelBuilder modelBuilder) { migrationBuilder.InsertData( table: "Blogs", columns: new[] { "Url" }, values: new object[] { "http://generated.com" }); }Custom initialization logic:
csusing (var context = new DataSeedingContext()) { context.Database.EnsureCreated(); var testBlog = context.Blogs.FirstOrDefault(b => b.Url == "http://test.com"); if (testBlog == null) { context.Blogs.Add(new Blog { Url = "http://test.com" }); } context.SaveChanges(); }
Perform migration and update database.
Example: Create an extension class for data seeding, this keeps the DbContext class clean:
public static class DataSeed(this ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().HasData(
new Blog { BlogId = 1, Url = "http://sample.com" },
new Blog { BlogId = 2, Url = "https://example.io" }
);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
migrationBuilder.DataSeed();
}Cascading referential integrity constraint
- No Action (Update/Delete action cannot be performed prevented)
- Cascade
- Set NULL
- Set Default
On Update: default is No Action On Delete: default is Cascade
Model Class
Class properties can have annotations
Annotations
Annotations as derived from
System.ComponentModel.DataAnnotationsNot all database related properties can be configured using annotations
For more control use
OnModelCreatingmethodExample:
csprotected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Blog>().Property(b => b.Title).HasMaxLength(50).IsRequired(); } // we can achive the same using annotations on the property `Title` in model class `Blog` [MaxLength(50)] [Required] public string Title { get; set; }
Fluent API
Fluent API is an advanced way of specifying model configuration that covers everything that data annotations can do in addition to some more advanced configuration not possible with data annotations.
- Fluent API is another way to configure your domain classes.
If both Data annotations and Fluent API are use the precedence is:
- Fluent API > Data Annotations > Default Conventions
The Code First Fluent API is most commonly accessed by overriding the OnModelCreating method on your derived DbContext.
- Fluent API provides more functionality for configuration than DataAnnotations. Fluent API supports the following types of mappings.
Data Transfer Object (DTO)
The contract enabled between the client and the service
