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を組み立てます。
混同しやすい名前の対応関係
| 名前 | 例 | 役割 | ここがズレると起きること |
|---|---|---|---|
| エンティティクラス名 | Occupation | LINQで扱う型 | 規約でテーブル名推測に使われることが多い |
| DbSetプロパティ名 | Occupations | DbContextから参照する入口 | 規約や設定によりテーブル名に影響する場合がある |
| 実DBのテーブル名 | Occupation | SQL 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.config | connectionStringsの name と Initial Catalog | DbContextの名前と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が主キーとして認識しやすい列名
| パターン | 例 | 補足 |
|---|---|---|
| Id | Id | 最もシンプル |
| <クラス名>Id | OccupationId | 慣習として多い |
主キーが慣習とズレている場合の指定例
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");
}

コメント