0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ASP.NET Core + EF Core + PostgreSQL で `42P01: リレーション"Products"は存在しません` が出たときの原因と対処

0
Posted at

はじめに

ASP.NET Core MVC アプリから PostgreSQL に接続し、一覧画面を開いたところ、次の例外が発生しました。

PostgresException: 42P01: リレーション"Products"は存在しません

PostgreSQL 上にはデータベースもテーブルも存在しているのに、なぜこのエラーになるのか——原因は PostgreSQL の大文字小文字の扱いEF Core のデフォルト命名 の不一致でした。

本記事では、実際に行った対処手順をまとめます。

環境

  • ASP.NET Core MVC
  • Entity Framework Core
  • PostgreSQL
  • Npgsql.EntityFrameworkCore.PostgreSQL

接続先(appsettings.json):

"ConnectionStrings": {
  "DefaultConnection": "Host=localhost;Port=5432;Database=sample_db;Username=postgres;Password=***"
}

DB 側には手動で作成した products テーブル(小文字)が存在していました。

発生したエラー

PostgresException: 42P01: リレーション"Products"は存在しません

42P01 は PostgreSQL の 「定義されたリレーション(テーブル等)が存在しない」 エラーです。

原因の整理:PostgreSQL は大文字小文字をどう扱うか

PostgreSQL では、識別子(テーブル名・カラム名)の扱いが次のように分かれます。

SQL の書き方 実際の名前 大文字小文字
CREATE TABLE products (...) products 引用符なし → 小文字に正規化
CREATE TABLE Products (...) products 同上(結果は小文字)
CREATE TABLE "Products" (...) Products 引用符付き → 大小区別

EF Core(Npgsql)はデフォルトで、エンティティ名や DbSet 名から "Products"(ダブルクォート付き・大文字始まり) というテーブル名で SQL を発行します。

一方、手動作成したテーブルは products(小文字) です。

EF Core が探すもの:  "Products"
DB にあるもの:       products
→ 別物として扱われる → 42P01

「テーブルはあるのに存在しないと言われる」 のは、この名前の不一致が原因でした。

同様に、C# モデルのプロパティ名(PascalCase)と DB のカラム名(小文字)が一致しない場合も、 カラム不存在エラー になります。

対処 1:コンテキストでテーブル名を DB に合わせる

AppDbContextDbSet 名や EF Core のデフォルト命名では "Products" を参照してしまうため、OnModelCreating実際のテーブル名 products を明示 しました。

修正前(イメージ)

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
    public DbSet<Product> Products { get; set; }
    // → EF Core は "Products" テーブルを探しに行く
}

修正後

using Microsoft.EntityFrameworkCore;

namespace SampleApp.Data
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
        public DbSet<Product> Products { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // PostgreSQL 上の実テーブル名(小文字)を明示
            modelBuilder.Entity<Product>().ToTable("products");
        }
    }
}

補足

  • DbSet プロパティ名(Products)と DB のテーブル名(products)は別物 です。
  • ToTable("products") により、生成される SQL が実 DB のテーブル名と一致します。
  • 代替手段として、モデルクラスに [Table("products")] を付ける方法もあります。

対処 2:Models に [Column] でカラム名を DB に合わせる

テーブル名を直したあと、今度は カラム名の不一致 が問題になりました。

C# モデル(プロパティ名) DB カラム名
Id id
Name name
Price price
CreatedAt created_at

EF Core はデフォルトでプロパティ名をそのままカラム名として使うため、PostgreSQL 側の小文字カラム名と一致しません。

修正後(Product.cs

using System;
using System.ComponentModel.DataAnnotations.Schema;

namespace SampleApp.Models;

public class Product
{
    [Column("id")]
    public int Id { get; set; }

    [Column("name")]
    public string Name { get; set; } = String.Empty;

    [Column("price")]
    public int Price { get; set; }

    [Column("created_at")]
    public DateTime CreatedAt { get; set; }
}

System.ComponentModel.DataAnnotations.Schema[Column("...")] で、C# 側のプロパティ名と DB 側のカラム名を対応付け できます。

代替手段

カラムマッピングは AppDbContext.OnModelCreating にまとめることもできます。

modelBuilder.Entity<Product>(entity =>
{
    entity.ToTable("products");
    entity.Property(e => e.Id).HasColumnName("id");
    entity.Property(e => e.Name).HasColumnName("name");
    entity.Property(e => e.Price).HasColumnName("price");
    entity.Property(e => e.CreatedAt).HasColumnName("created_at");
});

属性方式と OnModelCreating 方式は どちらか一方 で十分です。

対応の流れ(まとめ)

  1. 一覧画面にアクセス → 42P01: リレーション"Products"は存在しません
  2. DB には products テーブルがあることを確認
  3. 原因: EF Core が "Products" を参照、DB は products(大小区別で不一致)
  4. 対処: AppDbContextToTable("products") を追加
  5. 次の問題: カラム名も C#(PascalCase)と DB(小文字)で不一致
  6. 対処: Product モデルに [Column("...")] を追加
  7. アプリ再起動 → 一覧画面が表示される

再発防止のヒント

手動でテーブルを作る場合

  • PostgreSQL では 小文字・snake_case で統一するのが一般的
  • EF Core 側は ToTable / [Table] / [Column] / HasColumnName で明示的にマッピングする

EF Core マイグレーションを使う場合

dotnet ef migrations add InitialCreate
dotnet ef database update

マイグレーションでスキーマを作れば、EF Core が期待するテーブル・カラム名と DB が自動的に揃います。
手動作成テーブルEF マイグレーション を混在させると、今回のような不一致が起きやすいです。

DB の実態確認コマンド(psql)

\c sample_db
\d products

テーブル名・カラム名・型を、モデル定義と突き合わせると原因切り分けが早いです。

おわりに

42P01: リレーション"Products"は存在しません は、一見「テーブルが無い」ように見えますが、PostgreSQL の引用符付き識別子による大文字小文字の違い が原因であることが多いです。

今回の対応は次の 2 点でした。

  1. AppDbContextToTable("products") を指定(テーブル名の一致)
  2. Product モデルに [Column("...")] を追加(カラム名の一致)

手動で作った PostgreSQL テーブルを EF Core から使う場合は、命名規則の差を最初からマッピングで吸収しておくと安全です。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?