Entity Framework Core with Code First
Code-First with Minimum Configuration
This example uses SQL Server.
1Install Packages
Create a new console application and install two packages.
Install-Package Microsoft.EntityFrameworkCore.Tools
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Used to execute Entity Framework commands in the Package Manager Console.Microsoft.EntityFrameworkCore.SqlServer
The provider library for using SQL Server.
2Create DbContext
Entity Framework will automatically detect a class inheriting DbContext
, allowing you to perform code-first operations. No specific code needs to be written at the program's entry point. However, if there are no tables to be created, effectively nothing will be executed. Therefore, create a Hoge
class and add a property of type DbSet<Hoge>
to the class inheriting DbContext
.
public class AppDbContext : DbContext
{
public DbSet<Hoge> Hoge { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer("<接続文字列>"); // <Connection String>
}
public class Hoge
{
public int Id { get; set; }
public string Name { get; set; }
}
3Create a Migration
- Build the project
- Set the console app as the startup project
- Open the Package Manager Console and select the console app as the default project
Once these conditions are met, execute the following command:
Add-Migration hoge
If the output is as follows, it's OK:
PM> Add-Migration hoge
Build started...
Build succeeded.
To undo this action, use Remove-Migration.
The automatically generated source file will be displayed. The file name will be yyyyMMddHHmmss_<migration_name>.cs
. This file is created in a folder named Migrations
, which is automatically generated. This C# code defines the database changes.
4Apply the Migration to the Database
Next, execute the following command:
Update-Database
If the output is as follows, the application is complete:
PM> Update-Database
Build started...
Build succeeded.
Acquiring an exclusive lock for migration application. See https://aka.ms/efcore-docs-migrations-lock for more information if this takes too long.
Applying migration 'yyyyMMddHHmmss_hoge'.
Done.
If you connect to the database, you can confirm that the Hoge table has been created.
In Case of a Web Application
Similar to console applications, code-first can be used in a single project, but it is better to split the projects as follows:
- (A) ASP.NET Core application
- (B) Data access library
- (C) Model (table) definition
The project dependencies are as follows:
(A) ⇒ (B), (C)
(B) ⇒ (C)
If implementing in a single project, simply create each of the following code pieces within that one project.
(A) ASP.NET Core Application
The method remains the same for both Razor Pages
and MVC
.
- Startup project
- Program.cs (add DbContext DI settings)
- appsettings.json (connection string)
Example: Adding DbContext (PostgreSQL)
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add DbContext DI settings
builder.Services.AddDbContext<DefaultDbContext>(options =>
{
options.UseNpgsql(builder.Configuration.GetDefaultConnectionString());
options.EnableSensitiveDataLogging();
});
Example: Connection string
// appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=timecard;Username=postgres;Password=hoge;Port=5432"
},
(B) Data Access Library
- Migrations (*.cs) auto-generated
- DefaultDbContext.cs
- DefaultDbContext.DbSet.cs
- DefaultDbContext.OnModelCreating.cs
- Database provider (select according to the database used)
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Sqlite
- Npgsql.EntityFrameworkCore.PostgreSQL
- Oracle.EntityFrameworkCore
It is often easier to maintain if you make the DbContext class partial and separate it by purpose.
Example: DefaultDbContext (PostgreSQL)
// DefaultDbContext.cs
public partial class DefaultDbContext : DbContext
{
public DefaultDbContext(DbContextOptions options)
: base(options)
{
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
}
}
// DefaultDbContext.DbSet.cs
public partial class DefaultDbContext
{
public DbSet<User> User { get; set; }
public DbSet<UserCard> UserCard { get; set; }
public DbSet<TimeCard> TimeCard { get; set; }
}
// DefaultDbContext.OnModelCreating.cs
public partial class DefaultDbContext
{
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<User>(e => { e.HasKey(e => new { e.UserId }); });
builder.Entity<UserCard>(e => { e.HasKey(e => new { e.UserId, e.CardNumber }); });
builder.Entity<TimeCard>(e => { e.HasKey(e => new { e.UserId, e.Date }); });
// Convert names to SnakeCase
builder.ToSnakeNames();
}
}
(C) Model (Table) Definition
- Models/Entities (*.cs)
Create the model classes that will become tables here.
(A)'s ViewModel/PageModel: Definition of screen display items
(C)'s Model: Definition of database tables
The key point is to completely separate these roles and establish the dependency (A) ⇒ (C), so that even if there are changes to the screen display specifications, the database table definitions are not directly affected.
Creating Migrations and Applying to the Database
- Create/Modify models
- Build the solution
- Display the Package Manager Console
- Confirm that "(B) Data Access Library" is selected in the Package Manager Console's "Default project"
- Confirm that "(A) ASP.NET Core Application" is selected as the startup project
- Execute the following command
Add-Migration <migration_name>
<migration_name>
is arbitrary but must be unique within the Migrations folder. 1
At this point, a *.cs
file defining the changes will be created in the (B) Data Access Library/Migrations
folder.
- Execute the following command:
Update-Database
Model changes will be applied to the database.
Footnotes
-
The file name will be
yyyyMMddHHmmss_<migration_name>.cs
, but the class name in the code will not have a timestamp. You will executeAdd-Migration
each time you apply model changes to the database, but the same<migration_name>
cannot be used multiple times, so it's a good idea to decide on a naming convention for migration names in advance.↩