ASP.NET Core 7で「ConnectionString property has not been initialized」エラーの原因と解決策(DbContext・appsettings設定)

ASP.NET Core 7(Entity Framework Core)でデバッグ実行すると System.InvalidOperationException: The ConnectionString property has not been initialized. が発生する場合、原因の多くは「接続文字列の取得キー」と「appsettings.json の配置場所」が噛み合っていないことです。この記事では、なぜ起きるのかを整理し、すぐ直せる修正案と運用まで見据えたベストプラクティスをまとめます。

目次

発生しているエラーの状況(再現しやすい形で整理)

質問のケースは、DbContext を 2 つ登録していて、片方は動くのにもう片方(QA 用)が落ちる、というパターンです。

builder.Services.AddDbContext<QsccDevAlphaDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddDbContext<QSCC_QAContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("QA_Context")));

appsettings.json 側は次のように、DefaultConnection は ConnectionStrings にある一方、QA_Context が別セクション ConnectionStringQA に入っています。

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=...;Database=QsccDevAlpha;..."
  },
  "ConnectionStringQA": {
    "QA_Context": "Data Source=...;Initial Catalog=QSCC_QA;..."
  }
}

この状態で QA_Context を使った処理に入ると、接続文字列が設定されておらず、結果として The ConnectionString property has not been initialized. が発生します。


直接の原因:GetConnectionString が探しに行く場所が決まっている

builder.Configuration.GetConnectionString("QA_Context") は、見た目以上に「探す場所」が固定されています。具体的には、内部的に次のキーを探します。

  • ConnectionStrings:QA_Context

ところが実際の設定ファイルでは、QA_Context は ConnectionStringQA 配下にあります。つまり、構造がこうなっています。

  • 存在する:ConnectionStringQA:QA_Context
  • 存在しない:ConnectionStrings:QA_Context

その結果、発生している流れは次の通りです。

手順何が起きているか結果
1GetConnectionString("QA_Context") を呼ぶ内部で ConnectionStrings:QA_Context を参照
2ConnectionStrings:QA_Context が存在しない戻り値が null になる
3UseSqlServer(null) 相当の状態で DbContext が構成される接続文字列未設定のまま
4実際に DB 接続が必要なタイミング(クエリ実行、SaveChanges など)The ConnectionString property has not been initialized. が発生

ポイントは、エラーが「登録時」ではなく「DB を触る瞬間」に出ることが多く、原因が見えにくいことです。特に DbContext が複数あると、片方だけ失敗するので混乱しやすくなります。


修正方法その1:今の構成のまま、正しいキーで読み取る

構成を変えたくない(すぐ直したい)なら、GetConnectionString を使わず、現在の階層に合わせてキーを指定して読み取ります。

builder.Services.AddDbContext<QSCC_QAContext>(options =>
    options.UseSqlServer(builder.Configuration["ConnectionStringQA:QA_Context"]));

IConfiguration は セクション名:キー の形で階層アクセスできます。これは appsettings の一般的な値取得と同じ考え方です。

より「読み取れなかった時に気づける」書き方にするなら、null チェックも合わせると安全です。

var qaConn = builder.Configuration["ConnectionStringQA:QA_Context"];
if (string.IsNullOrWhiteSpace(qaConn))
{
    throw new InvalidOperationException("ConnectionStringQA:QA_Context が設定されていません。appsettings を確認してください。");
}

builder.Services.AddDbContext<QSCC_QAContext>(options =>
    options.UseSqlServer(qaConn));

「実行してから落ちる」のではなく「起動時に落として原因を特定する」ことで、調査コストが一気に下がります。


修正方法その2:おすすめ構成に寄せて、ConnectionStrings に統一する

ASP.NET Core では、接続文字列は ConnectionStrings セクションにまとめるのが定番です。この形に揃えると、GetConnectionString が素直に使えて、チーム開発でも「どこを見ればいいか」が統一されます。

appsettings.json を次のように修正します。

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=...;Database=QsccDevAlpha;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True;Encrypt=True",
    "QA_Context": "Data Source=...;Initial Catalog=QSCC_QA;Integrated Security=True;TrustServerCertificate=True;MultipleActiveResultSets=true;Encrypt=True"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

すると Program.cs はそのまま(またはより自然に)書けます。

builder.Services.AddDbContext<QSCC_QAContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("QA_Context")));

接続文字列の取得方法が統一されると、次のようなメリットが出ます。

  • 「どのキーをどこに置くか」が迷子にならない
  • 環境変数(ConnectionStrings__XXX)で上書きしやすい
  • User Secrets、Azure Key Vault などへ移行しやすい
  • レビュー時に気づきやすい(ConnectionStrings を見れば済む)

GetConnectionString と Configuration[“…”] の違いを整理

今回つまずきやすいポイントは、「どちらも文字列が取れそうなのに、参照先が違う」点です。違いを表で押さえておくと、今後の事故が減ります。

取得方法参照するキー向いている用途注意点
GetConnectionString("Name")ConnectionStrings:Name接続文字列を ConnectionStrings に統一して運用する別セクションに置くと null になりやすい
Configuration["A:B"]A:B(指定した場所そのまま)任意のセクションから値を取るキー名が散らばると管理が難しくなる
GetSection("A").GetValue<string>("B")A:Bセクションを明示して読み取りたい書き方が少し冗長になりがち

DbContext が2つ必要?それとも「環境切替」なら1つで足りる?

質問コードでは DbContext を 2 つ登録していますが、ここは設計上の分岐点になります。

「環境ごとに DB が違うだけ」(開発 DB / QA DB / 本番 DB に接続先が切り替わるだけ)なら、通常は DbContext は 1 つで十分です。接続文字列を環境別に変えるのが ASP.NET Core の王道です。

一方、次のような理由があるなら DbContext を分けるのは正当です。

状況DbContext を分けるべき?理由
同じアプリが「別DB(別スキーマ・別権限)」へ同時アクセスする分けることが多いモデル・マイグレーション・接続設定が別管理になるため
テナント別DBなど、実行時に接続先が動的に変わるケース次第Factory パターンや接続文字列解決が必要になる
単に「開発/QA/本番」で接続先が違う分けないのが一般的環境別 appsettings で安全に切替でき、コードが増えない

もし今回が「QA 用に別の DB を持っているだけ(環境差)」であれば、DbContext を増やすより、環境別 appsettings に寄せた方が保守が楽です。


環境別(開発・QA・本番)の切り分けベストプラクティス

ASP.NET Core は、環境名(例:Development / Staging / Production など)に応じて appsettings.{Environment}.json を自動で読み込み、設定を上書きする仕組みが用意されています。

例として、すべて同じキー名 ConnectionStrings:DefaultConnection を使い、ファイルだけ分けます。

// appsettings.json(開発)
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=DEV-SQL;Database=MyAppDev;..."
  }
}
// appsettings.QA.json(QA)
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=QA-SQL;Database=MyAppQA;..."
  }
}
// appsettings.Production.json(本番)
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=PROD-SQL;Database=MyApp;..."
  }
}

Program.cs は常に同じで OK です。

builder.Services.AddDbContext<MyAppContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

環境別に切り替えるメリットは「コードが増えない」「事故が起きにくい」「レビューで見つけやすい」です。特に接続文字列は人が触る頻度が高いので、ルール化しておくと効果が出ます。

設定の上書き順序を理解すると、原因調査が速くなる

「appsettings に書いたのに反映されない」という相談はよくあります。多くの場合、どこかで上書きされています。代表的な優先順位を表で整理します(後ろほど優先度が高い想定です)。

優先度設定ソース例よくある落とし穴
低appsettings.json全環境共通の既定値本番情報を置いてしまう(避ける)
appsettings.{Env}.jsonQA / Production の差分環境名のスペル違いで読まれない
User Secrets(開発向け)ローカルだけの秘密情報チームに共有されない(意図通りならOK)
環境変数ConnectionStrings__DefaultConnectionCI/CD で意図せず上書きされる
高コマンドライン引数--ConnectionStrings:DefaultConnection=...デバッグ時だけ別値になって混乱する

環境変数で接続文字列を上書きする場合のキー書き方

Docker やサーバー運用では、接続文字列を appsettings に置かず、環境変数で渡すことも多いです。このとき、階層の区切りは「:」ではなく「__(アンダースコア2つ)」になります。

appsettings のキー環境変数名例
ConnectionStrings:QA_ContextConnectionStrings__QA_Contextサーバー側で安全に差し替え可能
ConnectionStringQA:QA_ContextConnectionStringQA__QA_Context独自セクションでも上書きは可能だが運用ルールが必要

今回のケースで「最短で直す」実践手順

現場でありがちな「まず直して、次に整える」の順でまとめます。

手順1:値が取れているかを起動時に確認する

まず、起動時に接続文字列が取れているかだけ確認します(本番ではログ出力に注意)。

var defaultConn = builder.Configuration.GetConnectionString("DefaultConnection");
var qaConn1 = builder.Configuration.GetConnectionString("QA_Context"); // null の可能性
var qaConn2 = builder.Configuration["ConnectionStringQA:QA_Context"];  // こちらは取れるはず

if (string.IsNullOrWhiteSpace(defaultConn))
{
    throw new InvalidOperationException("DefaultConnection が設定されていません。");
}
if (string.IsNullOrWhiteSpace(qaConn2))
{
    throw new InvalidOperationException("ConnectionStringQA:QA_Context が設定されていません。");
}

「null だった」ことが確定すれば、原因はほぼ設定階層の不一致です。

手順2:どちらの修正方針を取るか決める

  • すぐ直すだけなら:Configuration[“ConnectionStringQA:QA_Context”] に変更
  • 運用まで見据えるなら:QA_Context を ConnectionStrings に移す

チーム開発や将来の引継ぎを考えると、長期的には ConnectionStrings に統一が安定します。


補足:DbContext の「引数なしコンストラクタ」があると別ルートで事故る

今回の根本原因は設定キーですが、同じエラーメッセージは別の原因でも起こります。その代表が DbContext を DI 経由ではなく引数なしで生成してしまうパターンです。

例えば、次のように引数なしコンストラクタがあると、誤って new QSCC_QAContext() の経路が生まれます。すると DbContextOptions が渡されず、接続文字列も設定されません。

public partial class QSCC_QAContext : DbContext
{
    public QSCC_QAContext() { } // 基本的に不要(事故の入口)

    public QSCC_QAContext(DbContextOptions<QSCC_QAContext> options)
        : base(options)
    {
    }
}

推奨は、引数なしコンストラクタを削除し、DI で生成される経路だけに寄せることです。

public partial class QSCC_QAContext : DbContext
{
    public QSCC_QAContext(DbContextOptions<QSCC_QAContext> options)
        : base(options)
    {
    }
}

もしスキャフォールド(Scaffold-DbContext)で生成された DbContext を使っている場合、元のテンプレートに OnConfiguring が入っていることがあります。DI で管理する方針なら、OnConfiguring に接続文字列を直書きしない(または残す場合も「DI で指定されていなければ」の条件付きにする)など、運用ルールを決めておくと混乱しません。


補足:dotnet ef(マイグレーション)実行時だけ落ちる場合の見方

アプリの実行ではなく、dotnet ef migrations add や dotnet ef database update のときに同様のエラーが出る場合、原因は少し変わります。

  • Design-time(設計時)に DbContext を作るための情報が不足している
  • 環境変数(ASPNETCORE_ENVIRONMENT)が想定と違い、読み込む appsettings が違う
  • 複数 DbContext があり、どれを使うか曖昧になっている

この場合は IDesignTimeDbContextFactory<TContext> を用意して、設計時の生成経路を明示すると安定します(複数 DbContext 構成では特に効果があります)。

public class QSCC_QAContextFactory : IDesignTimeDbContextFactory<QSCC_QAContext>
{
    public QSCC_QAContext CreateDbContext(string[] args)
    {
        var config = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false)
            .AddEnvironmentVariables()
            .Build();

        var conn = config.GetConnectionString("QA_Context"); // ConnectionStrings に寄せておくとシンプル
        var options = new DbContextOptionsBuilder<QSCC_QAContext>()
            .UseSqlServer(conn)
            .Options;

        return new QSCC_QAContext(options);
    }
}

「アプリは動くのに ef だけ動かない」という状況は、設定の読み込み元が違うことが多いので、設計時の入口を固定すると切り分けが容易になります。


トラブルシューティングチェックリスト

最後に、今回のエラーに遭遇したときのチェック観点をまとめます。原因が 1 つとは限らないため、再発防止の観点でも役立ちます。

チェック項目確認方法よくある対処
GetConnectionString の参照先が ConnectionStrings かappsettings.json の階層を見るConnectionStrings に統一する/キーを Configuration["A:B"] で読む
キー名が一致しているか(スペル、記号、余計な空白)設定ファイルとコードを並べて確認キーを統一、命名規則を決める
環境別 appsettings が意図通り読まれているかASPNETCORE_ENVIRONMENT を確認launchSettings.json / 環境変数の設定を見直す
User Secrets や環境変数で上書きされていないかCI/CD やローカル設定を確認上書きルールを明文化、不要な設定を削除
DbContext を DI ではなく new していないか生成箇所を検索引数なしコンストラクタを削除、DI に統一
複数 DbContext のうち、想定した方が使われているかDI 登録と注入箇所を確認命名・役割を明確化、必要なら Factory を導入

まとめ:今回の結論と、今後の運用で強くなるポイント

  • 今回の直接原因は、QA_Context の接続文字列が ConnectionStrings 配下に無く、GetConnectionString(“QA_Context”) が null を返していたことです。
  • 修正は大きく2択です。
    • すぐ直す:builder.Configuration["ConnectionStringQA:QA_Context"] のように階層を合わせて読む
    • おすすめ:接続文字列を ConnectionStrings に統一し、GetConnectionString を正しく使う
  • さらに安定させるなら、環境別 appsettings(appsettings.QA.json など)で接続先を切替し、コードは変えない運用に寄せると事故が減ります。
  • 同じエラーは 引数なしコンストラクタや DI を通さない DbContext 生成でも起きるため、DbContext の生成経路を DI に統一するのが安全です。

この記事を書いた人

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

コメント

コメントする

目次