SQL Serverにテーブル追加後、Entity Framework(DbContext)でInvalid object name dbo.Occupationsになる原因と解決策

SQL Serverに新テーブルを追加したのに、Entity Framework(DbContext)から参照できず「Invalid object name ‘dbo.Occupations’」が出る。多くはテーブル名の複数形ルールや接続先DBの取り違えが原因です。.edmxが無いプロジェクトでも、切り分けから修正までを具体例つきで解説します。

目次

エラーの正体:EFが探しているのは「dbo.Occupations」

例外メッセージの Invalid object name ‘dbo.Occupations’ は、SQL Serverが「接続しているデータベースの中に、その名前のオブジェクト(テーブル/ビューなど)が見つからない」と判断したときに返します。つまり、LINQのJOINの書き方そのものより先に、EFが生成したSQLが参照しているテーブル名と、実際のDBに存在するテーブル名が噛み合っていません。

このケースで特に多いのは次の2系統です。

系統よくある状況エラーメッセージの特徴
テーブル名の不一致DBは Occupation(単数) なのに、EFが Occupations(複数) を探している「dbo.Occupations」のように複数形が出る
接続先DBの不一致テーブルを追加したDBとは別のDB(別環境/別インスタンス/LocalDBなど)に接続している同じ名前の他テーブルは動くが、新規追加だけ見つからない/本番だけ失敗する等

最短で切り分けるチェックリスト

迷ったら、次の順番で確認すると最短で原因に辿り着きます(上ほど頻出です)。

チェック項目確認のしかた当たりならこう直す
DB側の実テーブル名が「Occupation」か「Occupations」かSSMSでオブジェクト名を確認/下記SQLで検索EF側で ToTable / [Table] で明示マッピング
接続先データベース名(Initial Catalog / Database)が正しいかweb.configの接続文字列を確認/実行時ログでDataSource・Databaseを出す接続文字列を修正(環境別config/変換も含めて)
スキーマがdbo以外になっていないかsys.tablesでschema_idを確認ToTableで スキーマ指定(例:ToTable(“Occupation”,”hr”))
大文字小文字が厳密なDB(CSコレーション)になっていないかDBの照合順序(collation)を確認DBの実名に合わせてマッピング(大小文字も一致させる)
接続文字列が見つからず、EFが別DBを自動作成していないかSQL Serverに予期しないDB(Context名など)が増えていないか確認DbContextの接続文字列名を明示/初期化戦略を見直す

DB側で「Occupation」が本当にあるかを1発で確認するSQL

まずは、テーブル名・スキーマ名・存在DBを事実ベースで確定させます。

-- 現在の接続DB名
SELECT DB_NAME() AS CurrentDatabase;

-- Occupation/Occupations を含むテーブルを検索
SELECT
SCHEMA_NAME(t.schema_id) AS SchemaName,
t.name AS TableName
FROM sys.tables AS t
WHERE t.name LIKE '%Occupation%'
ORDER BY SchemaName, TableName;

原因の本命:Occupation と Occupations の名前ズレ

EF(特にEF6のCode First系)では、既定の規約で エンティティ名からテーブル名を推測します。多くのプロジェクトで「単数のクラス名 → 複数形のテーブル名」という規約(Pluralizing)が有効になっているため、Occupation クラスを追加すると、DBに Occupations テーブルがある前提でSQLを組み立てます。

混同しやすい名前の対応関係

名前例役割ここがズレると起きること
エンティティクラス名OccupationLINQで扱う型規約でテーブル名推測に使われることが多い
DbSetプロパティ名OccupationsDbContextから参照する入口規約や設定によりテーブル名に影響する場合がある
実DBのテーブル名OccupationSQL Serverの実体一致していないとInvalid object nameになる
スキーマ名dboテーブルの所属dbo以外なら dbo.Occupations は見つからない

対処の基本方針

対処は大きく2つです。既存DBの都合(すでに単数テーブルで運用している、SQLやストアドが単数前提など)を考えると、アプリ側で明示マッピングする方が安全なことが多いです。

対処向いているケースメリット注意点
DBのテーブル名を変更する(Occupation → Occupations)新規テーブルで依存が少ないEFの既定規約に沿ってシンプルSQL/既存コード/権限/外部連携の影響を受けやすい
EF側でテーブル名を明示マッピングする既存DBの命名規約を維持したいDB側は変えずに済む/影響範囲が限定的追加したエンティティごとに設定が必要

EF6(ASP.NET Web Formsで多い構成)での具体的な修正方法

ここからは、.edmxが存在しない(Database Firstではなく、Code First/Code First from Database系の構成)を前提に、手元のコードだけで直す方法をまとめます。コード例は VB.NET を中心にしていますが、C#でも考え方は同じです。

方法:OnModelCreating で ToTable を指定する(最もコントロールしやすい)

DbContextでマッピングを明示すると、EFが探すテーブル名を確実に固定できます。スキーマも併せて指定可能です。

Imports System.Data.Entity
Imports System.Data.Entity.ModelConfiguration.Conventions

Public Class CCDBContext
    Inherits DbContext

    Public Property Occupations As DbSet(Of Occupation)

    Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
        MyBase.OnModelCreating(modelBuilder)

        ' 既存DBの実テーブル名が単数の場合
        modelBuilder.Entity(Of Occupation)().ToTable("Occupation")

        ' dbo以外のスキーマなら、スキーマも明示
        ' modelBuilder.Entity(Of Occupation)().ToTable("Occupation", "hr")

        ' もしDB全体が単数テーブルで統一されているなら、複数形規約を無効化する選択肢もある
        ' modelBuilder.Conventions.Remove(Of PluralizingTableNameConvention)()
    End Sub
End Class

方法:Table属性(アノテーション)で指定する(設定が分散しやすいが手軽)

エンティティクラス側でテーブル名を固定したい場合は、Table属性で指定できます。スキーマ指定も可能です。

Imports System.ComponentModel.DataAnnotations
Imports System.ComponentModel.DataAnnotations.Schema

<Table("Occupation")>
Public Class Occupation
    <Key>
    Public Property OccupationId As Integer

    Public Property Name As String
End Class

もしテーブルがdbo以外のスキーマにあるなら次のようにします。

<Table("Occupation", Schema:="hr")>
Public Class Occupation
    <Key>
    Public Property OccupationId As Integer
End Class

方法:複数形規約を無効化する(全体に効くので影響範囲に注意)

既存DBが「単数テーブル名」で統一されている場合、毎回ToTableを書くより、複数形変換を止めた方が運用が楽になることがあります。ただし、すでに複数形前提で動いているエンティティがあると影響が広がるため、プロジェクト全体の命名規約を確認してから適用してください。

Imports System.Data.Entity.ModelConfiguration.Conventions

Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
    MyBase.OnModelCreating(modelBuilder)

    modelBuilder.Conventions.Remove(Of PluralizingTableNameConvention)()
End Sub

もう一つの本命:接続先DBの取り違え(Web Formsで特に起きやすい)

DB側に確かにテーブルを作ったのに見つからない場合、次に疑うべきは アプリが接続しているDBが別物 というパターンです。Web Formsは、ローカル実行・IIS Express・IIS・本番サーバーで設定の置き場所が分かれやすく、同名の接続文字列が複数存在していたり、web.config変換で差し替わっていたりします。

web.configの接続文字列例(nameの一致が重要)

DbContext側で name=CCDBContext を指定しているなら、web.configのconnectionStringsにも同じnameが必要です。

<connectionStrings>
  <add name="CCDBContext"
       connectionString="Data Source=SERVERNAME;Initial Catalog=YourDatabase;Integrated Security=True;MultipleActiveResultSets=True"
       providerName="System.Data.SqlClient" />
</connectionStrings>

実行中のアプリが「どこに繋いでいるか」をコードで可視化する

推測をやめて、実行時にDataSourceとDatabase名をログに出すと一瞬で切り分けできます。

Imports System.Diagnostics

Using ctx As New CCDBContext()
    Dim dataSource = ctx.Database.Connection.DataSource
    Dim databaseName = ctx.Database.Connection.Database

    Trace.WriteLine("EF DataSource: " & dataSource)
    Trace.WriteLine("EF Database : " & databaseName)
End Using

ログに出たDB名が、Occupationテーブルを追加したDBと一致していなければ、接続文字列(Initial Catalog / Database)の修正が必要です。

DbContextのコンストラクタも確認する

DbContextのコンストラクタを引数なしで呼んでいる場合、EFは「クラス名と同じ名前の接続文字列」を探します。見つからないと、環境によってはLocalDBなどに接続してしまい、結果的にテーブルが無いDBを見てしまうことがあります。

Public Class CCDBContext
    Inherits DbContext


Public Sub New()
    ' 接続文字列名を明示すると、環境差分の事故が減る
    MyBase.New("name=CCDBContext")
End Sub


End Class

接続文字列で見るべきポイント

見る場所見る項目典型的な事故
web.configconnectionStringsの name と Initial CatalogDbContextの名前とconnectionString名が一致せず、別DBに接続
Web.config変換(Release/環境別)差し替え後の接続先開発はOKだが本番だけ別DBで落ちる
IISのアプリケーションプールID/権限SQL Serverのログイン/権限別ユーザーで接続していて、想定外のDBが見えている
LocalDB/SQL Expressのインスタンス(localdb)\\MSSQLLocalDB など手元SSMSは本物のSQL Server、アプリはLocalDBというズレ

スキーマの取り違え:dboではない場所に作っていないか

エラーが dbo.Occupations となっている時点で、EFは「dboスキーマにあるはず」と決め打ちでSQLを投げています。もし実テーブルが hr.Occupation のように別スキーマで作られていると、名前自体は合っていても見つかりません。

スキーマを含めて実体を確認するSQL

SELECT
    s.name AS SchemaName,
    t.name AS TableName
FROM sys.tables AS t
INNER JOIN sys.schemas AS s ON s.schema_id = t.schema_id
WHERE t.name IN ('Occupation', 'Occupations')
ORDER BY s.name, t.name;

スキーマをEF側で合わせる

Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
    MyBase.OnModelCreating(modelBuilder)


modelBuilder.Entity(Of Occupation)().ToTable("Occupation", "hr")


End Sub

「.edmxが無い」プロジェクトでの更新アプローチ

「Update Model from Database」が使えない=壊れている、ではありません。.edmxが無い構成は、主に次のどれかです。

構成特徴今回のテーブル追加でやること
Code Firstコードが正、DBはマイグレーションで追従させる前提本来はマイグレーションで作成。ただし既にDBで作ったならToTable等で合わせる
Code First from Database(逆生成)既存DBからDbContext/クラスを自動生成して使う再生成(または差分だけ手作業追加)。上書きに注意
手書きモデル(軽量ORM的運用)必要なテーブルだけDbSetとクラスを手で足している今回と同様に、DbSet/クラス/マッピングを手で追加

既存のWeb Formsプロジェクトで安定運用しているなら、いきなり全再生成するより、差分(Occupationだけ)を手で追加して、テーブル名を明示マッピングするのが安全なことが多いです。

JOINで失敗する前に確認したい「主キー」

今回の例外は「テーブルが見つからない」なので、まず名前・DB・スキーマを直すのが先決です。ただし、その次に起きやすいのが 主キーが無い/EFが主キーを推測できない 問題です。Occupationテーブルに主キーが無いと、EFはエンティティとして追跡できず、別の例外になります。

EFが主キーとして認識しやすい列名

パターン例補足
IdId最もシンプル
<クラス名>IdOccupationId慣習として多い

主キーが慣習とズレている場合の指定例

Imports System.ComponentModel.DataAnnotations

Public Class Occupation

Public Property Code As String


Public Property Name As String


End Class

EFが発行しているSQLを見れば、原因はほぼ確定する

「本当にEFがdbo.Occupationsを叩いているのか」を確認したい場合は、SQLログを出すのが確実です。Web Formsでも、DbContext生成直後にログ出力を設定できます。

Imports System.Diagnostics

Public Class CCDBContext
    Inherits DbContext

    Public Sub New()
        MyBase.New("name=CCDBContext") ' 例:接続文字列名を明示
        Me.Database.Log = Sub(s) Trace.WriteLine(s)
    End Sub

    Public Property Occupations As DbSet(Of Occupation)
End Class

ログに FROM [dbo].[Occupations] のようなSQLが出ていれば、名前ズレが原因であることがほぼ確定します。逆にDB名が想定外なら接続先の問題です。

実務で役立つ「ありがちな落とし穴」まとめ

落とし穴起きること回避策
テーブル名が単数なのに、EFが複数形で探すInvalid object name ‘dbo.Occupations’ToTable / Table属性で明示マッピング
スキーマがdboではないdbo.~を探して見つからないToTable(“Occupation”,”schema”)で合わせる
接続文字列が別環境を指している開発はOK、本番だけNGなど実行時にDataSource/Databaseをログ出し
web.configに接続文字列が無く、EFが別DBを作る思いもよらないDBに接続しているDbContextでname=~を明示/設定の配置を統一
主キーが無い/推測できない別の例外(キー定義のエラー)になるPK追加 or Key属性/HasKeyで明示
DBが大文字小文字を区別するOccupation と occupation が別物扱いDBの実名に厳密に合わせる

結局どれを直せばいいのか:今回のケースの最短ルート

例外に dbo.Occupations と出ているなら、まずは次の流れが最短です。

  • SSMSで「Occupation」テーブルの 正確な名前(単数/複数) と スキーマ を確定する
  • アプリ実行中の 接続先DB名 をログで出して一致を確認する
  • 一致しているのにエラーが続くなら、DbContextで ToTable(“Occupation”)(必要ならスキーマも)を追加する
  • 次の段階として、主キーやリレーション設定(JOIN対象列)を確認する

「.edmxが無いから更新できない」と悩みがちですが、Code First系では、DbSet追加+エンティティクラス追加+マッピング(ToTable/Table属性)が更新そのものです。ここを押さえると、新規テーブル追加時のトラブル対応が一気に楽になります。

参考:EF Coreでも考え方は同じ

もしEntity Framework Coreを使っている場合でも、テーブル名の明示マッピングは同様に行えます。Web FormsではEF6が多いものの、移行や共存の現場では混在することもあるため、考え方だけ押さえておくと安心です。

// EF Core(C#)例
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Occupation>().ToTable("Occupation");
}

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次