tags: PostgreSQL

前言

紀錄在 Entity Framework Core(EF Core) 操作 PostgreSQL 資料庫的一些步驟和細節

環境

  • .NET 6
  • PostgreSQL 15
  • EF Core 7

前置作業

想要透過 EF Core 存取資料庫,我們需要先到Nuget上安裝對應的套件,也必須要有相對應的資料庫提供者也就是Provider,透過引用不同的提供者讓 EF Core 可以存取不同的資料庫,在 Postgre 上我使用的提供者是 Npgsql.EntityFrameworkCore.PostgreSQL

以下是這是會用到的套件列表

1
2
3
4
5
6
7
dotnet add package Microsoft.EntityFrameworkCore --version 7.0.4
dotnet add package Microsoft.EntityFrameworkCore.Abstractions --version 7.0.4
dotnet add package Microsoft.EntityFrameworkCore.Analyzers --version 7.0.4
dotnet add package Microsoft.EntityFrameworkCore.Design --version 7.0.4
dotnet add package Microsoft.EntityFrameworkCore.Relational --version 7.0.4
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL --version 7.0.3
dotnet add package EFCore.NamingConventions --version 7.0.2

DbContext

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
        .UseNpgsql("Host=localhost;Database=efcoresample;Username=user;Password=password")
        .UseSnakeCaseNamingConvention();
}
public class Blog
{
    public int Id { get; set; }
    public string Url { get; set; }
    public List<Post> Posts { get; set; }
}
public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

這邊我使用官方文件上的範例來說明,要在C#和資料庫做溝通,我們需要建立一個類別並且繼承DbContext,並且在其中利用DbSet定義我們需要的資料表,Blog Post則是資料表的欄位定義模型也就是Entity

其中 OnConfiguring 方法,可以用來定義資料庫的連線字串,但更好的方法是從外部注入,做法是在 Program.cs 中加入以下的程式碼

1
2
3
4
builder.Services.AddDbContext<BloggingContext>(
    options => options
    .UseNpgsql(Configuration.GetConnectionString("BloggingContext"))
    .UseSnakeCaseNamingConvention());

其中的 BloggingContext 是連線字串的名稱,資料庫連線字串則是寫在 appsetting.json

1
2
3
4
5
6
{
  "ConnectionStrings": {
      "BloggingContext":
      "Host=localhost;Database=efcoresample;Username=user;Password=password"
  },
}

UseSnakeCaseNamingConvention

在C#的命名慣例上我們習慣使用駝峰式命名法(camel case),但在Postgresql上的命名慣例則是蛇形命名法(snake case),如果我們沒有在DbContext 上指定命名慣例,Migration時會按照C#來慣例來為資料表命名

駝峰式命名法(camel case)

單字之間不分開,又分成大駝峰(每一個單字首字大寫)、小駝峰(首字小寫後續單字首字大寫)

1
2
大駝峰 UseSnakeCaseNamingConvention
小駝峰 useSnakeCaseNamingConvention

蛇式命名法(snake case)

單字與單字之間透過_連接,單字首字皆為小寫

1
use_snake_case_naming_convention

我覺得這個非常重要,我在剛接觸的時候沒有注意到命名的問題,再開發的時候使用pgAdmin 一直想說為什麼下SQL時資料表以及欄位名稱都要加"",後來才知道命名會影響語法的格式

假設我的 Table 名稱是 BlogPost

1
SELECT * FROM "BlogPost"

Table 名稱為 blog_post

1
SELECT * FROM blog_post

OnModelCreating

如果想要對資料庫做更加細部的設定,我們可以複寫 OnModelCreating 這個方法

屬性生成

UseSerialColumns

若主鍵欄位是數字並希望可以自動增加,下方的寫法可以做全域的的設定

1
2
protected override void OnModelCreating(ModelBuilder modelBuilder)
    => modelBuilder.UseSerialColumns();
Guid

當主鍵是Guid時,Ef Core的預設行爲是在客戶端產生Guid後送到資料庫,這邊用Blog來舉例,它有一個欄位叫做BlogGuid型別是Guid,需要在建立時自動的產生

1
2
3
4
protected override void OnModelCreating(ModelBuilder modelBuilder)
    => modelBuilder.Entity<Blog>()
    .Property(b => b.BlogGuid)
    .HasValueGenerator<GuidValueGenerator>();

就如預設的行為,這會在客戶端產生並且送入資料庫,若希望在資料庫內產生也是可以

1
2
3
4
5
6
7
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
    .Entity<Blog>()
    .Property(e => e.BlogGuid)
    .HasDefaultValueSql("gen_random_uuid()");
}

這樣寫就會在資料庫呼叫gen_random_uuid方法去產生Guid

索引 Index

為了提升效能,索引是一個非常常用的方法,下面的語法是為Blog資料表的url欄位建立索引的寫法

1
2
protected override void OnModelCreating(ModelBuilder modelBuilder)
    => modelBuilder.Entity<Blog>().HasIndex(x => x.Url);

如果想要使用postgresql 11include index可以這樣寫

1
2
3
4
protected override void OnModelCreating(ModelBuilder modelBuilder)
    => modelBuilder.Entity<Blog>()
    .HasIndex(x => x.Id)
    .IncludeProperties(x => x.url);

執行移轉(Migration)

當我們完成Entity以及DbContext的設定後,最後一步就是把我們的設定套用到資料庫上。

首先使用這行指令建立新的 migration

1
dotnet ef migrations add InitialCreate

再使用下列語法套用資料庫的更新

1
dotnet ef database update

完成後專案內會出現 Migrations 資料夾,並且Postgresql內也會出現對應的資料庫以及資料表