LoginSignup
4
1

More than 1 year has passed since last update.

Blazorで何故か IConfiguration を使うところでビルドエラー、の謎を解く

Last updated at Posted at 2022-12-24

「CS1929:'IConfiguration' に 'GetConnectionString' の定義が含まれていません」

先日、Qiita で Blazor に関する下記記事を見つけました。

IConfiguration.razor というファイル名で Razor コンポーネントを記述し、その中で Microsoft.Extensions.Configuration.IConfiguration オブジェクトを DI コンテナから注入してもらって使うコードを書いたら、
「CS1929:'IConfiguration' に 'GetConnectionString' の定義が含まれておらず、最も適している拡張メソッド オーバーロード 'ConfigurationExtensions.GetConnectionString(IConfiguration, string)' には 'IConfiguration' 型のレシーバーが必要です」
というビルドエラーになってしまった、というお話でした。

なぜこんなビルドエラーになってしまうのか、ちょっと紐解いてみましょう。

.razor は、"ちょっと変わった構文の C# ソースファイル"

まず重要なヒントとして、.razor ファイルは C# のソースコードに変換されている」 ということがあります。先日下記記事を Qiita に投稿したのですが、その中に同じく ".razor ファイルは C# のソースコードに変換されている" という名前のセクションで、このことを少し説明してあります。

つまり、.razor ファイルというのは、(ComponentBae クラスから派生した) C# のクラス実装を生成する、ちょっと変わった構文の C# ソースファイル、と捉えてみることができる、ということですね。

で、その .razor ファイルの名前が、生成される C# クラスの名前になる、という仕様になっております。例えば Foo.razor というファイル名で Razor コンポーネントを実装したら、生成される C# コード上、そのクラス名は Foo になるんですね。ちなみに名前空間は、とくに明示的に指定なき場合は、<アセンブリ名>.<その Razor ファイルが配置されているサブフォルダ> になります (名前空間を明示的に指定するには @namespace ディレクティブが使えます)。

この情報を基に、冒頭で説明したビルドエラーが発生する下記 Razor コンポーネントのソースコードを、

Pages/IConfiguration.razor
@page "/IConfiguration"
@inject IConfiguration config

pg_con = @connection_str

@code {
  private string? connection_str;

  protected override void OnInitialized()
  {
    connection_str = config.GetConnectionString("db_con");
  }
}

C# ソースコードの体裁に書き直してみると、下記のようになります。

Pages/IConfiguration.razor から生成されるC#ソースコードに相当するもの
namespace {アセンブリ名}.Pages;

[Route("/IConfiguration")]
public class IConfiguration : ComponentBase
{
  [Inject] 
  private IConfiguration config { get; set; }

  private string? connection_str;

  protected override void OnInitialized()
  {
    connection_str = config.GetConnectionString("db_con");
  }
  // ... 以下略...

さて、上記 C# コードを見たときに、お気づきでしょうか。DI コンテナから注入してもらうつもりの 「Microsoft.Extensions.Configuration.IConfiguration」と、この Razor コンポーネントのクラス名「{アセンブリ名}.Pages.IConfiguration」 とで、型名が被ってしまっていることを!

その結果、以下のように解釈されて、CS1929 ビルドエラーになってしまうわけですね。

Pages/IConfiguration.razor から生成されるC#ソースコードに相当するもの
...
// ⚠️この Razor コンポーネントのクラス名が、"IConfiguration" になっている!
public class IConfiguration : ComponentBase
{
  [Inject] 
  private IConfiguration config { get; set; }
  //   ここ👆 の IConfiguration は、名前空間 Microsoft.Extensions.Configuration
  //   にあるそれではなく、この Razor コンポーネント自身のクラス名であると解釈される!
  ...
  protected override void OnInitialized()
  {
    connection_str = config.GetConnectionString("db_con");
    // この Razor コンポーネント自身には 👆 "GetConnectionString()"
    // などというメソッドは当然存在しないので、それで CS1929 ビルドエラー発生!🔥
  }
  // ... 以下略...

Visual Studio で開発している場合は、下図のとおり、「@inject IConfiguration config」のところでマウスホバーさせてツールチップを表示させると、「Microsoft.Extensions.Configuration.IConfiguration」ではなくて、「{アセンブリ名}.Pages.IConfiguration」のほうを指していることを知ることができます。

image.png

回避策

ビルドエラーの原因、その正体がわかれば、回避策も考えられます。

まず最初に思いつくのは、冒頭で紹介した記事内で既に解決されているとおり、.razor のファイル名を変える (結果として Razor コンポーネントのクラス名が IConfiguration ではなくなり、型名の被りが解消される) ことで解決できます。

他に考えられる方法としては、@inject ディレクティブで注入してもらう型名 IConfiguration を、完全修飾型名で指定する、つまり名前空間込みで型名を記述する方法も考えられます。実際のコーディング例は下記です。

Pages/IConfiguration.razor
...
@inject Microsoft.Extensions.Configuration.IConfiguration config
<!-- @Inject ディレクティブで指定する型名を、👆 名前空間込みで記述する -->
...

まとめ

以上、「.razor は、"ちょっと変わった構文の C# ソースファイル"」という意識で Razor コンポーネントのコーディングに取り組むことで、ビルドエラーが発生してもその原因を特定しやすくなるのではないか、との期待があります。

Happy Coding :)

4
1
2

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
4
1