「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 コンポーネントのソースコードを、
@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# ソースコードの体裁に書き直してみると、下記のようになります。
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 ビルドエラーになってしまうわけですね。
...
// ⚠️この 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」のほうを指していることを知ることができます。
回避策
ビルドエラーの原因、その正体がわかれば、回避策も考えられます。
まず最初に思いつくのは、冒頭で紹介した記事内で既に解決されているとおり、.razor
のファイル名を変える (結果として Razor コンポーネントのクラス名が IConfiguration
ではなくなり、型名の被りが解消される) ことで解決できます。
他に考えられる方法としては、@inject
ディレクティブで注入してもらう型名 IConfiguration
を、完全修飾型名で指定する、つまり名前空間込みで型名を記述する方法も考えられます。実際のコーディング例は下記です。
...
@inject Microsoft.Extensions.Configuration.IConfiguration config
<!-- @Inject ディレクティブで指定する型名を、👆 名前空間込みで記述する -->
...
まとめ
以上、「.razor
は、"ちょっと変わった構文の C# ソースファイル"」という意識で Razor コンポーネントのコーディングに取り組むことで、ビルドエラーが発生してもその原因を特定しやすくなるのではないか、との期待があります。
Happy Coding :)