EF Core 9 (EF9) での破壊的変更
このページでは、EF Core 8 から EF Core 9 に更新する既存のアプリケーションを破壊する可能性がある API と動作の変更について説明します。 以前のバージョンの EF Core から更新する場合は、以前の破壊的変更について確認するようにしてください。
ターゲット フレームワーク
EF Core 9 は .NET 8 をターゲットとします。 つまり、NET 8 をターゲットとする既存のアプリケーションは引き続きこのバージョンをターゲットにできます。 以前の .NET、.NET Core、および .NET Framework のバージョンをターゲットとしているアプリケーションは、EF Core 9 を使用するためには .NET 8 または .NET 9 をターゲットとする必要があります。
まとめ
Note
Azure Cosmos DB を使用している場合、 Azure Cosmos DB の破壊的変更に関する以下の別個のセクションをご覧ください。
影響が大きい変更
保留中のモデルの変更がある場合に移行を適用すると例外がスローされる
以前の動作
前回の移行と比較して、モデルに保留中の変更がある場合、それらは Migrate
が呼び出されたときに、残りの移行では適用されません。
新しい動作
EF Core 9.0 以降では、前回の移行と比較して、モデルに保留中の変更がある場合、dotnet ef database update
、Migrate
、または MigrateAsync
が呼び出されると例外がスローされます。
コンテキスト 'DbContext' のモデルには保留中の変更があります。 データベースを更新する前に、新しい移行を追加してください。 この例外は、イベント ID 'RelationalEventId.PendingModelChangesWarning' を 'DbContext.OnConfiguring' または 'AddDbContext' の 'ConfigureWarnings' メソッドに渡すことによって抑制またはログに記録できます。
理由
モデルの変更後に新しい移行の追加を忘れることはよくある間違いであり、場合によっては診断が難しい場合があります。 この新しい例外により、移行が適用された後に、アプリのモデルがデータベースと一致することを確認できます。
軽減策
この例外が発生する可能性のある一般的な状況がいくつかあります。
- 移行が一切ない。 これは、データベースが他の方法で更新される場合に一般的です。
- 軽減策: データベース スキーマの管理に移行を使用する予定がない場合は、
Migrate
またはMigrateAsync
の呼び出しを削除し、それ以外の場合は移行を追加します。
- 軽減策: データベース スキーマの管理に移行を使用する予定がない場合は、
- 少なくとも 1 つの移行があるが、モデル スナップショットがない。 これは、手動で作成された移行で一般的です。
- 軽減策: EF ツールを使用して新しい移行を追加すると、モデル スナップショットが更新されます。
- モデルは開発者によって変更されなかったが、決定論的でない方法で構築されているため、EF が変更済みとして検出する。 これは、
new DateTime()
、DateTime.Now
、DateTime.UtcNow
、またはGuid.NewGuid()
がHasData()
に指定されたオブジェクトで使用される場合に一般的です。- 軽減策: 新しい移行を追加し、その内容を調べて原因を特定し、動的データをモデル内の静的なハードコーディングされた値に置き換えます。 移行は、モデルが修正された後に再作成する必要があります。 動的データをシード処理に使用する必要がある場合は、
HasData()
ではなく、新しいシード処理のパターンを使用することを検討してください。
- 軽減策: 新しい移行を追加し、その内容を調べて原因を特定し、動的データをモデル内の静的なハードコーディングされた値に置き換えます。 移行は、モデルが修正された後に再作成する必要があります。 動的データをシード処理に使用する必要がある場合は、
- 最後の移行が、移行の適用のために使用されたものとは異なるプロバイダー用に作成された。
- 軽減策: これはサポートされていないシナリオです。 次のコード スニペットを使用して警告を抑制できますが、このシナリオでは将来の EF Core リリースで動作が停止する可能性があります。 推奨される解決策は、プロバイダーごとに個別の移行セットを生成することです。
- 移行が、一部の EF サービスを置き換えることによって動的に生成される、または選択される。
軽減策: この場合、警告は誤検知であり、抑制する必要があります。
options.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning))
シナリオが上記のどのケースにも該当せず、新しい移行を追加すると毎回同じ移行が作成されるか、または空の移行が生成され、依然として例外がスローされる場合は、小さな再現プロジェクトを作成し、新しい issue で EF チームと共有してください。
明示的なトランザクションで移行を適用すると例外がスローされる
以前の動作
移行を回復性のある方法で適用するため、一般的には次のパターンが使用されていました。
await dbContext.Database.CreateExecutionStrategy().ExecuteAsync(async () =>
{
await using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken);
await dbContext.Database.MigrateAsync(cancellationToken);
await transaction.CommitAsync(cancellationToken);
});
新しい動作
EF Core 9.0 以降では、Migrate
と MigrateAsync
の呼び出しによってトランザクションが開始され、ExecutionStrategy
を使用してコマンドが実行され、アプリで上記のパターンが使用されている場合は例外がスローされます。
'Microsoft.EntityFrameworkCore.Migrations.MigrationsUserTransactionWarning': 移行を適用する前にトランザクションが開始されましたという警告でエラーが生成されました。 これにより、データベース ロックが取得されなくなるため、同時移行アプリケーションからデータベースが保護されなくなります。 トランザクションと実行戦略は、必要に応じて EF によって既に管理されています。 外部トランザクションは削除してください。 この例外は、イベント ID 'RelationalEventId.MigrationsUserTransactionWarning' を 'DbContext.OnConfiguring' または 'AddDbContext' の 'ConfigureWarnings' メソッドに渡すことによって抑制またはログに記録できます。
理由
明示的なトランザクションを使用すると、データベース ロックを取得できなくなるため、データベースは同時移行アプリケーションから保護されず、EF でトランザクションを内部的に管理する方法も制限されます。
軽減策
トランザクション内にデータベース呼び出しが 1 つしかない場合は、外部トランザクションと ExecutionStrategy
を削除してください。
await dbContext.Database.MigrateAsync(cancellationToken);
それ以外の場合、シナリオで明示的なトランザクションが必要であり、同時移行アプリケーションを防ぐための他のメカニズムがある場合は、警告を無視してください。
options.ConfigureWarnings(w => w.Ignore(RelationalEventId.MigrationsUserTransactionWarning))
影響が中程度の変更
Microsoft.EntityFrameworkCore.Design
が EF ツールの使用時に見つかりません
以前の動作
以前は、EF ツールは次の方法で Microsoft.EntityFrameworkCore.Design
を参照する必要がありました。
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="*.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
新しい動作
.NET SDK 9.0.200 以降では、EF ツールを呼び出すと例外がスローされます。
ファイルまたはアセンブリ 'Microsoft.EntityFrameworkCore.Design, Culture=neutral, PublicKeyToken=null' を読み込めませんでした。 指定されたファイルが見つかりません。
理由
EF ツールは、生成された .deps.json
ファイルにプライベート資産が含まれる原因となった.NET SDK の文書化されていない動作に依存していました。 これは、sdk#45259で修正されました。 残念ながら、これを考慮した EF の変更は EF 9.0.x のサービス バーを満たしていないため、EF 10 で修正されます。
軽減策
EF 10 がリリースされる前の回避策として、Design
アセンブリ参照を公開可能としてマークできます。
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<Publish>true</Publish>
</PackageReference>
これには生成された .deps.json
ファイルに含まれますが、出力フォルダーと発行フォルダーに Microsoft.EntityFrameworkCore.Design.dll
をコピーする副作用があります。
影響が小さい変更
EF.Functions.Unhex()
により byte[]?
が返されるようになりました
以前の動作
EF.Functions.Unhex()
関数は以前は byte[]
を返すよう注釈が付けられていました。
新しい動作
EF Core 9.0 以降、Unhex() には byte[]?
を返すように注釈が付けられるようになりました。
理由
Unhex()
は SQLite unhex
関数に変換され、無効な入力に対しては NULL が返されます。 その結果、Unhex()
は注釈に違反して、これらのケースに対して null
を返しました。
軽減策
Unhex()
に渡されるテキスト コンテンツが有効な 16 進文字列を表していることが確実な場合、呼び出しで null が返されないというアサーションとして、null を許容する演算子を追加するだけです。
var binaryData = await context.Blogs.Select(b => EF.Functions.Unhex(b.HexString)!).ToListAsync();
それ以外の場合は、Unhex() の戻り値に対して null 値の実行時チェックを追加します。
SqlFunctionExpression の null 値許容引数のアリティを検証
以前の動作
以前は、異なる数の引数と null 値許容伝達引数で SqlFunctionExpression
を作成することが可能でした。
新しい動作
EF Core 9.0 以降、引数の数と null 値許容伝達引数が一致しない場合、EF はスローを行うようになりました。
理由
引数と null 値許容伝達引数の数が一致しないことで、予期しない動作が発生する可能性があります。
軽減策
argumentsPropagateNullability
が arguments
と同じ要素数を持っていることを確認します。 迷った場合は null 値許容引数に対して false
を使用します。
ToString()
メソッドが null
インスタンスの空の文字列を返すようになりました
以前の動作
以前は、引数値が ToString()
の場合、EF は null
メソッドに対して一貫性のない結果を返していました。 たとえば、値が ToString()
である bool?
プロパティの null
は null
を返しましたが、値が bool?
である非プロパティ null
式の場合は True
を返しました。 他のデータ型でも動作に一貫性がありませんでした。たとえば、 ToString()
値列挙の null
は空の文字列を返しました。
新しい動作
EF Core 9.0 以降では、引数値が ToString()
の場合、 null
メソッドは常に空の文字列を返すようになりました。
理由
以前の動作は、さまざまなデータ型や状況間で一貫性がなく、 C# の動作とも一致していませんでした。
軽減策
古い動作に戻すには、それに応じてクエリを書き直します。
var newBehavior = context.Entity.Select(x => x.NullableBool.ToString());
var oldBehavior = context.Entity.Select(x => x.NullableBool == null ? null : x.NullableBool.ToString());
共有フレームワークの依存関係が 9.0.x に更新されました
以前の動作
Microsoft.NET.Sdk.Web
SDK を使用し、net8.0 をターゲットとするアプリは、共有フレームワークから System.Text.Json
、 Microsoft.Extensions.Caching.Memory
、 Microsoft.Extensions.Configuration.Abstractions
、 Microsoft.Extensions.Logging
および Microsoft.Extensions.DependencyModel
などのパッケージを解決するため、通常、これらのアセンブリはアプリと共に展開されません。
新しい動作
EF Core 9.0 は引き続き net8.0 をサポートしていますが、現在は 9.0.x バージョンの System.Text.Json
、 Microsoft.Extensions.Caching.Memory
、 Microsoft.Extensions.Configuration.Abstractions
、 Microsoft.Extensions.Logging
および Microsoft.Extensions.DependencyModel
を参照しています。 net8.0 をターゲットとするアプリでは、これらのアセンブリのデプロイを回避するために共有フレームワークを利用することはできません。
理由
一致する依存関係バージョンには最新のセキュリティ修正プログラムが含まれており、それらを使用すると EF Core のサービス モデルが簡略化されます。
軽減策
前の動作を取得するには、アプリをターゲット net9.0 に変更します。
Azure Cosmos DB の破壊的変更
9.0 では、Azure Cosmos DB プロバイダーの改善に多大な労力が費やされました。 この変更には、影響が大きい破壊的変更が多数含まれています。既存のアプリケーションをアップグレードする場合、以下をよくお読みください。
重大な変更 | 影響 |
---|---|
$type ではなく Discriminator に変更されました |
高 |
既定では、id プロパティにディスクリミネーターが含まれるようになりました |
高 |
JSON id プロパティがキー にマップされる |
高 |
Azure Cosmos DB プロバイダー経由の同期 I/O はサポートされなくなりました | Medium |
SQL クエリで JSON 値を直接プロジェクションする必要があります | Medium |
未定義の結果がクエリ結果から自動的にフィルター処理されるようになりました | Medium |
誤って変換されたクエリは変換されなくなりました | Medium |
無視される代わりに HasIndex がスローされるようになりました |
低 |
9.0.0-rc.2 の後、IncludeRootDiscriminatorInJsonId は HasRootDiscriminatorInJsonId に名前が変更されました |
低 |
影響が大きい変更
ディスクリミネーター プロパティの名前が、$type
ではなく Discriminator
に変更されました
以前の動作
EF は、JSON ドキュメントにディスクリミネーター プロパティを自動的に追加し、ドキュメントが表すエンティティ型を識別します。 以前のバージョンの EF では、この JSON プロパティには既定で Discriminator
という名前が付けられていました。
新しい動作
EF Core 9.0 以降では、ディスクリミネーター プロパティは既定で $type
呼び出されるようになりました。 以前のバージョンの EF から得られた既存のドキュメントが Azure Cosmos DB にある場合、古い Discriminator
の命名が使用されており、EF 9.0 にアップグレードした後、それらのドキュメントに対するクエリは失敗します。
理由
新しい JSON プラクティスでは、ドキュメントの種類を識別する必要があるシナリオにおいて、$type
プロパティが使用されます。 たとえば、.NET の System.Text.Json もポリモーフィズムをサポートしており、$type
を既定の識別子プロパティ名として使用しています (ドキュメント)。 エコシステムの残りの部分に合わせて、外部ツールとの相互運用を容易にするため、既定値が変更されました。
軽減策
最も簡単な軽減策は、以前と同じように、ディスクリミネーター プロパティの名前を Discriminator
に構成するだけです。
modelBuilder.Entity<Session>().HasDiscriminator<string>("Discriminator");
これをすべての最上位レベルのエンティティ型に対して行うと、EF は以前と同じように動作します。
この時点で、必要に応じて、新しい $type
の命名を使用するようすべてのドキュメントを更新することもできます。
id
プロパティには、既定で EF キー プロパティのみ含まれるようになりました
以前の動作
以前は、EF はエンティティ型のディスクリミネーター値をドキュメントの id
プロパティに挿入していました。 たとえば、8 を含む Blog
プロパティを持つ Id
エンティティ型を保存した場合、JSON id
プロパティには Blog|8
が含められます。
新しい動作
EF Core 9.0 以降、JSON id
プロパティにはディスクリミネーター値が含められなくなり、キー プロパティの値のみが含められます。 上記の例では、JSON id
プロパティは単に 8
になります。 以前のバージョンの EF から得られた既存のドキュメントが Azure Cosmos DB にある場合、JSON id
プロパティにはディスクリミネーター値が含まれており、EF 9.0 にアップグレードした後、それらのドキュメントに対するクエリは失敗します。
理由
JSON id
プロパティは一意である必要があるため、同じキー値を持つ異なるエンティティが存在できるように、識別子が以前に追加されました。 たとえば、これにより、同じコンテナーとパーティション内に、値 8 を含む Blog
プロパティを持つ Post
と Id
の両方を持つことができるようになりました。 これは、各エンティティ型が独自のテーブルにマップされるため、独自のキースペースを持つリレーショナルデータベースデータモデリングパターンにより適しています。
EF 9.0 では、リレーショナル データベースからのユーザーの期待に応えるのではなく、一般的な Azure Cosmos DB NoSQL のプラクティスと期待に沿うようマッピングが全体的に変更されました。 さらに、id
プロパティにディスクリミネーターの値があると、外部ツールやシステムが EF で生成された JSON ドキュメントと対話することがより困難になります。このような外部システムは通常、既定で .NET 型から派生する EF ディスクリミネーターの値を認識しません。
軽減策
最も簡単な軽減策は、以前と同様、JSON id
プロパティにディスクリミネーターを含むよう EF を構成する方法です。 この目的のため、新しい構成オプションが導入されました。
modelBuilder.Entity<Session>().HasDiscriminatorInJsonId();
これをすべての最上位レベルのエンティティ型に対して行うと、EF は以前と同じように動作します。
この時点で、必要に応じて、すべてのドキュメントを更新して JSON id
プロパティを書き換えることもできます。 これは、異なる型のエンティティが同じコンテナー内で同じ ID 値を共有していない場合のみ可能です。
JSON id
プロパティがキーにマップされる
以前の動作
以前は、いずれかのプロパティが明示的に id
にマップされていない限り、EF は JSON id
プロパティにマップされたシャドウ プロパティを作成していました。
新しい動作
EF Core 9 以降では、可能な限り、キー プロパティは規則によって JSON id
プロパティにマップされます。 つまり、キー プロパティが同じ値を持つ別の名前でドキュメントに保持されなくなるため、ドキュメントを使用し、このプロパティの存在に依存する EF 以外のコードがあると、正しく機能しなくなります。
理由
EF 9.0 では、一般的な Azure Cosmos DB NoSQL のプラクティスと期待に応えるため、マッピングが一般的に変更されました。 また、キー値をドキュメントに 2 回格納することは一般的ではありません。
軽減策
EF Core 8 の動作を維持したい場合に最も簡単な軽減策は、この目的のために導入された新しい構成オプションを使用することです。
modelBuilder.Entity<Session>().HasShadowId();
これをすべての最上位レベルのエンティティ型に対して行うと、EF は以前と同じように動作します。 または、次を 1 回呼び出して、モデル内のすべてのエンティティ型に適用することもできます。
modelBuilder.HasShadowIds();
影響が中程度の変更
Azure Cosmos DB プロバイダー経由の同期 I/O はサポートされなくなりました
以前の動作
以前は、Azure Cosmos DB SDK に対して非同期呼び出しを実行する際に、ToList
や SaveChanges
などの同期メソッドを呼び出すことは、EF Core が .GetAwaiter().GetResult()
を使用して同期的にブロックする原因となっていました。 これにより、デッドロックが発生する可能性があります。
新しい動作
EF Core 9.0 以降では、同期 I/O の使用が試みられた場合に、EF が既定で例外をスローするようになりました。 例外メッセージは次のとおりです。"Azure Cosmos DB は同期 I/O をサポートしていません。 Entity Framework Core を使用して Azure Cosmos DB にアクセスする場合は、非同期メソッドのみを使用してそれを適切に待機するようにしてください。 詳細については、https://aka.ms/ef-cosmos-nosync を参照してください。"
理由
非同期メソッドでの同期ブロックはデッドロックを発生させる可能性があり、Azure Cosmos DB SDK がサポートしているのは非同期メソッドだけです。
軽減策
EF Core 9.0 において、このエラーは次のように抑制できます。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ConfigureWarnings(w => w.Ignore(CosmosEventId.SyncNotSupported));
}
しかしながら、同期 API は Azure Cosmos DB SDK ではサポートされていないため、アプリケーションは同期 API の使用を停止するべきです。 例外を抑制する機能は、EF Core の将来のリリースで削除され、その後は非同期 API の使用が唯一の選択肢になります。
SQL クエリで JSON 値を直接プロジェクションする必要があります
以前の動作
以前は、EF は次のようなクエリを生成していました。
SELECT c["City"] FROM root c
このようなクエリにより、以下のように Azure Cosmos DB によって各結果が JSON オブジェクトにラップされます。
[
{
"City": "Berlin"
},
{
"City": "México D.F."
}
]
新しい動作
EF Core 9.0 以降、EF は次のように VALUE
修飾子をクエリに追加するようになりました。
SELECT VALUE c["City"] FROM root c
このようなクエリにより、Azure Cosmos DB はラップされずに値を直接返します。
[
"Berlin",
"México D.F."
]
アプリケーションで SQL クエリを使用している場合、そのようなクエリには VALUE
修飾子が含まれていないため、EF 9.0 にアップグレードすると、そのクエリが壊れる可能性があります。
理由
各結果を追加の JSON オブジェクトにラップすると、シナリオによってはパフォーマンスが低下して、JSON 結果のペイロードが肥大化する可能性があるため、Azure Cosmos DB を操作する自然な方法ではありません。
軽減策
軽減するには、上に示すように、VALUE
修飾子を SQL クエリのプロジェクションに追加するだけでかまいません。
未定義の結果がクエリ結果から自動的にフィルター処理されるようになりました
以前の動作
以前は、EF は次のようなクエリを生成していました。
SELECT c["City"] FROM root c
このようなクエリにより、以下のように Azure Cosmos DB によって各結果が JSON オブジェクトにラップされます。
[
{
"City": "Berlin"
},
{
"City": "México D.F."
}
]
いずれかの結果が未定義の場合 (たとえば、City
プロパティがドキュメントに存在しない場合)、空のドキュメントが返され、EF はその結果の null
を返します。
新しい動作
EF Core 9.0 以降、EF は次のように VALUE
修飾子をクエリに追加するようになりました。
SELECT VALUE c["City"] FROM root c
このようなクエリにより、Azure Cosmos DB はラップされずに値を直接返します。
[
"Berlin",
"México D.F."
]
Azure Cosmos DB の動作では、 undefined
値が結果から自動的にフィルター処理されます。つまり、ドキュメントに City
プロパティのいずれかが存在しない場合、クエリは 2 つの結果 (1 つは null
) ではなく、1 つの結果のみを返します。
理由
各結果を追加の JSON オブジェクトにラップすると、シナリオによってはパフォーマンスが低下して、JSON 結果のペイロードが肥大化する可能性があるため、Azure Cosmos DB を操作する自然な方法ではありません。
軽減策
未定義の結果に対して null
値を取得することがアプリケーションにとって重要な場合、新しい undefined
演算子を使用して null
値を EF.Functions.Coalesce
に結合します。
var users = await context.Customer
.Select(c => EF.Functions.CoalesceUndefined(c.City, null))
.ToListAsync();
誤って変換されたクエリは変換されなくなりました
以前の動作
以前は、EF は次のようなクエリを変換していました。
var sessions = await context.Sessions
.Take(5)
.Where(s => s.Name.StartsWith("f"))
.ToListAsync();
ただし、このクエリの SQL 変換は正しくありません。
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Session") AND STARTSWITH(c["Name"], "f"))
OFFSET 0 LIMIT @__p_0
SQL では、WHERE
句は 句と OFFSET
句のLIMIT
評価されますが、上記の LINQ クエリでは、Take
演算子が Where
演算子の前に表示されます。 その結果、このようなクエリから正しくない結果が返される可能性があります。
新しい動作
EF Core 9.0 以降、このようなクエリは変換されなくなり、例外がスローされます。
理由
変換が正しくないと、サイレント データの破損が発生し、検出が困難な不具合がアプリケーションで発生する可能性があります。 EF は、データ破損を引き起こす可能性よりも、事前にスローすることでフェイルファストすることを常に優先します。
軽減策
前の動作に満足していて、同じ SQL を実行したい場合は、LINQ 演算子の順序を入れ替えるだけでかまいません。
var sessions = await context.Sessions
.Where(s => s.Name.StartsWith("f"))
.Take(5)
.ToListAsync();
残念ながら、Azure Cosmos DB では現在、SQL サブクエリの OFFSET
句と LIMIT
句はサポートされていません。これは、元の LINQ クエリの適切な変換に必要です。
影響が小さい変更
無視される代わりに HasIndex
がスローされるようになりました
以前の動作
以前は、HasIndex の呼び出しは EF Cosmos DB プロバイダーによって無視されていました。
新しい動作
HasIndex が指定されている場合、プロバイダーによりスローされるようになりました。
理由
Azure Cosmos DB では、すべてのプロパティに既定でインデックスが付けられます。インデックスを指定する必要はありません。 カスタム インデックス作成ポリシーを定義することはできますが、これは現在 EF ではサポートされていないため、EF のサポートなしで Azure Portal を使用して行うことができます。 HasIndex 呼び出しは何も行われないため、許可されなくなりました。
軽減策
HasIndex の呼び出しをすべて削除します。
9.0.0-rc.2 の後、IncludeRootDiscriminatorInJsonId
は HasRootDiscriminatorInJsonId
に名前が変更されました
以前の動作
IncludeRootDiscriminatorInJsonId
API は、9.0.0 rc.1 で導入されました。
新しい動作
EF Core 9.0 の最終リリースでは、API の名前が HasRootDiscriminatorInJsonId
に変更されました
理由
別の関連 API の名前が Has
ではなく Include
で始まるように変更されたため、一貫性を保つためにこの API の名前も変更されました。
軽減策
コードで IncludeRootDiscriminatorInJsonId
API を使用している場合、代わりに HasRootDiscriminatorInJsonId
を参照するよう変更するだけでかまいません。
.NET