チュートリアル: ASP.NET MVC アプリで EF を使用して関連データを読み取る
前のチュートリアルでは、School データ モデルを完了しました。 このチュートリアルでは、関連データ (Entity Framework がナビゲーション プロパティに読み込むデータ) を読み取って表示します。
以下の図は、使用するページを示しています。
Contoso University サンプル Web アプリケーションでは、Entity Framework 6 Code First と Visual Studio を使用して、ASP.NET MVC 5 アプリケーションを作成する方法を示します。 チュートリアル シリーズについては、シリーズの最初のチュートリアルをご覧ください。
このチュートリアルでは、次の作業を行いました。
- 関連データを読み込む方法を学習する
- Courses ページを作成する
- Instructors ページを作成する
前提条件
関連データを読み込む方法を学習する
Entity Framework で関連データをエンティティのナビゲーション プロパティに読み込むには、いくつかの方法があります。
遅延読み込み。 エンティティが最初に読み込まれるときに、関連データは取得されません。 ただし、ナビゲーション プロパティに初めてアクセスしようとすると、そのナビゲーション プロパティに必要なデータが自動的に取得されます。 その結果、複数のクエリがデータベースに送信されます。1 つはエンティティ自体用で、エンティティの関連データを取得するたびに 1 つずつ送信されます。 クラスは
DbContext
、既定で遅延読み込みを有効にします。一括読み込み。 エンティティが読み取られるときに、関連データがエンティティと共に取得されます。 これは通常、必要なデータをすべて取得する 1 つの結合クエリになります。 一括読み込みを指定するには、 メソッドを
Include
使用します。明示的読み込み。 これは遅延読み込みに似ていますが、コード内の関連データを明示的に取得する点が除きます。ナビゲーション プロパティにアクセスしても、自動的には発生しません。 関連データを手動で読み込むには、エンティティのオブジェクト状態マネージャー エントリを取得し、 コレクションの Collection.Load メソッドを呼び出すか、単一のエンティティを保持するプロパティの Reference.Load メソッドを呼び出します。 (次の例では、Administrator ナビゲーション プロパティを読み込む場合は、 を に
Reference(x => x.Administrator)
置き換えますCollection(x => x.Courses)
)。通常は、遅延読み込みをオフにした場合にのみ、明示的な読み込みを使用します。
プロパティ値はすぐには取得されないため、遅延読み込みと明示的読み込みはどちらも 遅延読み込みと呼ばれます。
パフォーマンスに関する考慮事項
取得したすべてのエンティティの関連データが必要な場合は、通常、データベースに送信された 1 つのクエリの方が、取得した各エンティティに対する分離したクエリよりも効率的なため、一括読み込みを使用すると、より最適なパフォーマンスが得られます。 たとえば、上記の例では、各部門に 10 個の関連コースがあるとします。 一括読み込みの例では、1 つの (結合) クエリと、データベースへの 1 回のラウンド トリップが発生します。 遅延読み込みと明示的な読み込みの例は、どちらも 11 個のクエリと 11 回のラウンド トリップでデータベースに送られます。 データベースとの余分なラウンド トリップは、特に待ち時間が長いときのパフォーマンスに悪影響をもたらします。
一方、一部のシナリオでは、遅延読み込みの方が効率的です。 一括読み込みでは、非常に複雑な結合が生成される可能性があり、SQL Server効率的に処理することはできません。 または、処理している一連のエンティティのサブセットに対してのみエンティティのナビゲーション プロパティにアクセスする必要がある場合は、一括読み込みが必要以上に多くのデータを取得するため、遅延読み込みの方がパフォーマンスが向上する可能性があります。 パフォーマンスが重要な場合、最適な選択を行うために、両方の方法でパフォーマンスをテストすることをお勧めします。
遅延読み込みでは、パフォーマンスの問題を引き起こすコードをマスクできます。 たとえば、一括読み込みまたは明示的読み込みを指定せず、大量のエンティティを処理し、各イテレーションで複数のナビゲーション プロパティを使用するコードは、非常に非効率的な場合があります (データベースへのラウンド トリップが多いため)。 オンプレミスの SQL サーバーを使用した開発で適切に実行されるアプリケーションでは、待機時間の増加と遅延読み込みが原因で、Azure SQL Database に移動するとパフォーマンスの問題が発生する可能性があります。 現実的なテスト負荷でデータベース クエリをプロファイリングすると、遅延読み込みが適切かどうかを判断するのに役立ちます。 詳細については、「エンティティ フレームワーク戦略の解明: 関連データの読み込み」および「Entity Framework を使用してネットワーク待機時間を短縮してSQL Azureする」を参照してください。
シリアル化の前に遅延読み込みを無効にする
シリアル化中に遅延読み込みを有効のままにすると、意図したよりも大幅に多くのデータのクエリが実行される可能性があります。 シリアル化は通常、型のインスタンスの各プロパティにアクセスすることによって機能します。 プロパティ アクセスによって遅延読み込みがトリガーされ、それらの遅延読み込みエンティティがシリアル化されます。 その後、シリアル化プロセスは遅延読み込みエンティティの各プロパティにアクセスし、さらに遅延読み込みとシリアル化を引き起こす可能性があります。 この連鎖反応を防ぐには、エンティティをシリアル化する前に遅延読み込みをオフにします。
シリアル化は、 高度なシナリオのチュートリアルで説明されているように、Entity Framework で使用されるプロキシ クラスによって複雑になる場合もあります。
シリアル化の問題を回避する方法の 1 つは、Entity Framework での Web API の使用 に関するチュートリアルに示すように、エンティティ オブジェクトではなくデータ転送オブジェクト (DTO) をシリアル化することです。
DTO を使用しない場合は、プロキシの作成を無効にすることで、遅延読み込みを無効にし、 プロキシの問題を回避できます。
遅延読み込みを無効にするその他の方法を次に示します。
特定のナビゲーション プロパティの場合は、 プロパティを
virtual
宣言するときにキーワード (keyword)を省略します。すべてのナビゲーション プロパティについて、 を に設定
LazyLoadingEnabled
しfalse
、コンテキスト クラスのコンストラクターに次のコードを配置します。this.Configuration.LazyLoadingEnabled = false;
Courses ページを作成する
Course
エンティティには、コースが割り当てられている部門の Department
エンティティを含む、ナビゲーション プロパティが含まれます。 コースの一覧に割り当てられた部署の名前を表示するには、ナビゲーション プロパティ内のエンティティから プロパティをDepartment
取得Name
するCourse.Department
必要があります。
前にコントローラーに対Course
して行った Entity Framework スキャフォールディングを使用して、ビューを含む MVC 5 コントローラーと同じオプションを使用して、エンティティ型に対して (CoursesController ではなく) という名前CourseController
のコントローラーをStudent
作成します。
設定 | 値 |
---|---|
Model クラス | [ コース (ContosoUniversity.Models)] を選択します。 |
データ コンテキスト クラス | [ SchoolContext (ContosoUniversity.DAL)] を選択します。 |
コントローラー名 | 「CourseController」と入力します。 繰り返しになりますが、 CoursesController と ではありません。 Course (ContosoUniversity.Models) を選択すると、コントローラー名の値が自動的に設定されました。 値を変更する必要があります。 |
その他の既定値のままにして、コントローラーを追加します。
Controllers\CourseController.cs を開き、 メソッドをIndex
見てください。
public ActionResult Index()
{
var courses = db.Courses.Include(c => c.Department);
return View(courses.ToList());
}
自動スキャフォールディングでは、Include
メソッドを使って、Department
ナビゲーション プロパティに一括読み込みを指定しています。
Views\Course\Index.cshtml を開き、テンプレート コードを次のコードに置き換えます。 変更が強調表示されています。
@model IEnumerable<ContosoUniversity.Models.Course>
@{
ViewBag.Title = "Courses";
}
<h2>Courses</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.CourseID)
</th>
<th>
@Html.DisplayNameFor(model => model.Title)
</th>
<th>
@Html.DisplayNameFor(model => model.Credits)
</th>
<th>
Department
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.CourseID)
</td>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Credits)
</td>
<td>
@Html.DisplayFor(modelItem => item.Department.Name)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.CourseID }) |
@Html.ActionLink("Details", "Details", new { id=item.CourseID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.CourseID })
</td>
</tr>
}
</table>
スキャフォールディング コードに、次の変更を行いました。
- 見出しが Index から Courses に変更されました。
CourseID
プロパティ値を示す Number 列が追加されました。 通常、主キーはエンド ユーザーにとって意味がないため、既定ではスキャフォールディングされません。 ただし、このケースでは、主キーは意味があり、表示する必要があります。- Department 列を右側に移動し、見出しを変更しました。 スキャフォールディングはエンティティの
Department
プロパティをName
正しく表示することを選択しましたが、ここでは [コース] ページで列見出しが [名前] ではなく Department である必要があります。
Department 列の場合、スキャフォールディングされたコードには、ナビゲーション プロパティに Name
読み込まれるエンティティの Department
プロパティが Department
表示されます。
<td>
@Html.DisplayFor(modelItem => item.Department.Name)
</td>
ページを実行して (Contoso University ホーム ページの [ コース ] タブを選択)、部門名を含む一覧を表示します。
Instructors ページを作成する
このセクションでは、Instructors ページを表示するために、エンティティのコントローラーとビュー Instructor
を作成します。 このページは、次の方法で関連データを読み取って表示します。
- インストラクターのリストには、
OfficeAssignment
エンティティからの関連データが表示されます。Instructor
エンティティとOfficeAssignment
エンティティは、一対ゼロまたは一対一のリレーションシップです。OfficeAssignment
エンティティに一括読み込みを使用します。 前述のように、通常、一括読み込みは、主テーブルで取得したすべての行の関連データが必要なときにより効率的です。 このケースでは、割り当てられたすべてのインストラクターのオフィスの割り当てを表示する必要があります。 - ユーザーがインストラクターを選択すると、関連する
Course
エンティティが表示されます。Instructor
エンティティとCourse
エンティティは多対多リレーションシップです。Course
エンティティとその関連Department
エンティティの一括読み込みを使用します。 この場合、選択したインストラクターに対してのみコースが必要になるため、遅延読み込みの方が効率的な場合があります。 ただし、この例では、ナビゲーション プロパティにあるエンティティ内のナビゲーション プロパティに一括読み込みを使用する方法を示します。 - ユーザーがコースを選択すると、
Enrollments
エンティティ セットからの関連データが表示されます。Course
エンティティとEnrollment
エンティティは一対多リレーションシップです。 エンティティとその関連Student
エンティティの明示的なEnrollment
読み込みを追加します。 (遅延読み込みが有効になっているため、明示的な読み込みは必要ありませんが、これは明示的な読み込みを行う方法を示しています)。
インストラクター インデックス ビューのビュー モデルを作成する
[Instructors] ページには、3 つの異なるテーブルが表示されます。 そのため、テーブルの 1 つにデータを保持するごとに、3 つのプロパティを含むビュー モデルを作成します。
ViewModels フォルダーで InstructorIndexData.cs を作成し、既存のコードを次のコードに置き換えます。
using System.Collections.Generic;
using ContosoUniversity.Models;
namespace ContosoUniversity.ViewModels
{
public class InstructorIndexData
{
public IEnumerable<Instructor> Instructors { get; set; }
public IEnumerable<Course> Courses { get; set; }
public IEnumerable<Enrollment> Enrollments { get; set; }
}
}
インストラクター コントローラーとビューを作成する
EF の InstructorController
読み取り/書き込みアクションを使用して (InstructorsController ではない) コントローラーを作成します。
設定 | 値 |
---|---|
Model クラス | [ Instructor (ContosoUniversity.Models)] を選択します。 |
データ コンテキスト クラス | [ SchoolContext (ContosoUniversity.DAL)] を選択します。 |
コントローラー名 | 「InstructorController」と入力します。 ここでも、 を持つ InstructorsControllerではありません。 Course (ContosoUniversity.Models) を選択すると、コントローラー名の値が自動的に設定されました。 値を変更する必要があります。 |
その他の既定値のままにして、コントローラーを追加します。
Controllers\InstructorController.cs を開き、 名前空間の using
ステートメントをViewModels
追加します。
using ContosoUniversity.ViewModels;
メソッドの Index
スキャフォールディングされたコードは、ナビゲーション プロパティに対 OfficeAssignment
してのみ一括読み込みを指定します。
public ActionResult Index()
{
var instructors = db.Instructors.Include(i => i.OfficeAssignment);
return View(instructors.ToList());
}
メソッドを Index
次のコードに置き換えて、追加の関連データを読み込み、ビュー モデルに配置します。
public ActionResult Index(int? id, int? courseID)
{
var viewModel = new InstructorIndexData();
viewModel.Instructors = db.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.Courses.Select(c => c.Department))
.OrderBy(i => i.LastName);
if (id != null)
{
ViewBag.InstructorID = id.Value;
viewModel.Courses = viewModel.Instructors.Where(
i => i.ID == id.Value).Single().Courses;
}
if (courseID != null)
{
ViewBag.CourseID = courseID.Value;
viewModel.Enrollments = viewModel.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
return View(viewModel);
}
メソッドは、オプションのルート データ (id
) と、選択したインストラクターと選択したコースの ID 値を提供するクエリ文字列パラメーター (courseID
) を受け取り、必要なすべてのデータをビューに渡します。 パラメーターは、ページの Select ハイパーリンクによって指定されます。
このコードは、ビュー モデルのインスタンスを作成し、インストラクターのリストに配置することから始めます。 このコードでは、 と Instructor.Courses
ナビゲーション プロパティのInstructor.OfficeAssignment
一括読み込みを指定します。
var viewModel = new InstructorIndexData();
viewModel.Instructors = db.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.Courses.Select(c => c.Department))
.OrderBy(i => i.LastName);
2 番目 Include
のメソッドは Courses を読み込み、読み込まれる各 Course に対してナビゲーション プロパティの Course.Department
一括読み込みを行います。
.Include(i => i.Courses.Select(c => c.Department))
前述のように、一括読み込みは必要ありませんが、パフォーマンスを向上させるために行われます。 ビューには常に OfficeAssignment
エンティティが必要なため、同じクエリでフェッチする方が効率的です。 Course
エンティティは、Web ページで講師が選択されている場合に必要です。そのため、一括読み込みは、ページが表示される頻度が高く、コースが選択されていない場合よりも頻繁に表示される場合にのみ、遅延読み込みよりも優れています。
インストラクター ID が選択されている場合は、選択したインストラクターがビュー モデルの講師の一覧から取得されます。 次に、ビュー モデルの Courses
プロパティが Course
エンティティと共にそのインストラクターの Courses
ナビゲーション プロパティから読み込まれます。
if (id != null)
{
ViewBag.InstructorID = id.Value;
viewModel.Courses = viewModel.Instructors.Where(i => i.ID == id.Value).Single().Courses;
}
メソッドは Where
コレクションを返しますが、この場合、そのメソッドに渡された条件によって返されるエンティティは 1 つだけ Instructor
になります。 Single
メソッドを実行すると、コレクションが、エンティティの Courses
プロパティへのアクセス権を付与する 1 つの Instructor
エンティティに変換されます。
コレクションに項目が 1 つしかないことがわかっている場合は、コレクションに 対して Single メソッドを使用します。 メソッドは Single
、渡されたコレクションが空の場合、または複数の項目がある場合に例外をスローします。 別の方法として SingleOrDefault があり、コレクションが空の場合は既定値 (null
この場合) を返します。 ただし、この場合でも例外が発生し (参照でnull
プロパティを検索Courses
しようとした場合)、例外メッセージは問題の原因を明確に示さなくなります。 メソッドを呼び出すときは、メソッドを Single
個別に Where
呼び出す代わりに、条件を Where
渡すこともできます。
.Single(i => i.ID == id.Value)
これは次のコードの代わりに使用します。
.Where(I => i.ID == id.Value).Single()
次に、コースが選択された場合、選択したコースはビュー モデルのコースのリストから取得されます。 その後、ビュー モデルの Enrollments
プロパティが、そのコースの Enrollment
ナビゲーション プロパティのエンティティと共に Enrollments
読み込まれます。
if (courseID != null)
{
ViewBag.CourseID = courseID.Value;
viewModel.Enrollments = viewModel.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
Instructor Index ビューを変更する
Views\Instructor\Index.cshtml で、テンプレート コードを次のコードに置き換えます。 変更が強調表示されています。
@model ContosoUniversity.ViewModels.InstructorIndexData
@{
ViewBag.Title = "Instructors";
}
<h2>Instructors</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>Last Name</th>
<th>First Name</th>
<th>Hire Date</th>
<th>Office</th>
<th></th>
</tr>
@foreach (var item in Model.Instructors)
{
string selectedRow = "";
if (item.ID == ViewBag.InstructorID)
{
selectedRow = "success";
}
<tr class="@selectedRow">
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.HireDate)
</td>
<td>
@if (item.OfficeAssignment != null)
{
@item.OfficeAssignment.Location
}
</td>
<td>
@Html.ActionLink("Select", "Index", new { id = item.ID }) |
@Html.ActionLink("Edit", "Edit", new { id = item.ID }) |
@Html.ActionLink("Details", "Details", new { id = item.ID }) |
@Html.ActionLink("Delete", "Delete", new { id = item.ID })
</td>
</tr>
}
</table>
既存のコードに次の変更を行いました。
モデル クラスが
InstructorIndexData
に変更されました。Index のページ タイトルが Instructors に変更されました。
が null でない場合
item.OfficeAssignment
にのみ表示されるitem.OfficeAssignment.Location
Office 列を追加しました。 (これは 1 対 0 または 1 のリレーションシップであるため、関連エンティティOfficeAssignment
がない可能性があります)。<td> @if (item.OfficeAssignment != null) { @item.OfficeAssignment.Location } </td>
選択したインストラクターの 要素に動的に追加
class="success"
するtr
コードを追加しました。 これにより、 Bootstrap クラスを使用して、選択した行の背景色が設定されます。string selectedRow = ""; if (item.InstructorID == ViewBag.InstructorID) { selectedRow = "success"; } <tr class="@selectedRow" valign="top">
各行の他のリンクの直前に Select というラベルが付いた新しい
ActionLink
ラベルが追加されました。これにより、選択したインストラクター ID が メソッドにIndex
送信されます。
アプリケーションを実行し、[Instructors] タブを選択します。関連エンティティがないOfficeAssignment
場合、ページには関連OfficeAssignment
エンティティのプロパティと空のテーブル セルが表示されますLocation
。
Views\Instructor\Index.cshtml ファイルで、終了table
要素 (ファイルの末尾) の後に、次のコードを追加します。 このコードでは、インストラクターが選択されたときに、インストラクターに関連するコースのリストを表示します。
@if (Model.Courses != null)
{
<h3>Courses Taught by Selected Instructor</h3>
<table class="table">
<tr>
<th></th>
<th>Number</th>
<th>Title</th>
<th>Department</th>
</tr>
@foreach (var item in Model.Courses)
{
string selectedRow = "";
if (item.CourseID == ViewBag.CourseID)
{
selectedRow = "success";
}
<tr class="@selectedRow">
<td>
@Html.ActionLink("Select", "Index", new { courseID = item.CourseID })
</td>
<td>
@item.CourseID
</td>
<td>
@item.Title
</td>
<td>
@item.Department.Name
</td>
</tr>
}
</table>
}
このコードでは、ビュー モデルの Courses
プロパティを読み取り、コースのリストを表示します。 また、選択したコースの ID をアクション メソッドに送信するIndex
ハイパーリンクも提供Select
されます。
ページを実行し、講師を選択します。 選択したインストラクターに割り当てられたコースを表示するグリッドを表示し、各コースに割り当てられた部門の名前を表示します。
追加したコード ブロックの後に、次のコードを追加します。 このコードは、コースを選択したときに、コースに登録されている受講者のリストを表示します。
@if (Model.Enrollments != null)
{
<h3>
Students Enrolled in Selected Course
</h3>
<table class="table">
<tr>
<th>Name</th>
<th>Grade</th>
</tr>
@foreach (var item in Model.Enrollments)
{
<tr>
<td>
@item.Student.FullName
</td>
<td>
@Html.DisplayFor(modelItem => item.Grade)
</td>
</tr>
}
</table>
}
このコードでは、コースに登録された受講生のリストを表示するために、ビュー モデルの Enrollments
プロパティを読み取ります。
ページを実行し、講師を選択します。 次に、コースを選択して、登録済みの受講者とその成績のリストを表示します。
明示的な読み込みの追加
InstructorController.cs を開き、選択したコースの登録の一覧をメソッドが取得する方法Index
を確認します。
if (courseID != null)
{
ViewBag.CourseID = courseID.Value;
viewModel.Enrollments = viewModel.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
講師の一覧を取得したときに、ナビゲーション プロパティと各コースの プロパティに対Department
してCourses
一括読み込みを指定しました。 次に、コレクションを Courses
ビュー モデルに配置し、そのコレクション内の Enrollments
1 つのエンティティからナビゲーション プロパティにアクセスします。 ナビゲーション プロパティに一括読み込みを指定していないため、遅延読み込みの Course.Enrollments
結果として、そのプロパティのデータがページに表示されます。
他の方法でコードを変更せずに遅延読み込みを無効にした場合、 Enrollments
コースが実際に持っていた登録数に関係なく、 プロパティは null になります。 その場合、プロパティを Enrollments
読み込むには、一括読み込みまたは明示的読み込みを指定する必要があります。 一括読み込みを行う方法を既に確認しました。 明示的な読み込みの例を確認するには、 メソッドを、プロパティを Index
明示的に読み込む次のコードに Enrollments
置き換えます。 変更されたコードが強調表示されています。
public ActionResult Index(int? id, int? courseID)
{
var viewModel = new InstructorIndexData();
viewModel.Instructors = db.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.Courses.Select(c => c.Department))
.OrderBy(i => i.LastName);
if (id != null)
{
ViewBag.InstructorID = id.Value;
viewModel.Courses = viewModel.Instructors.Where(
i => i.ID == id.Value).Single().Courses;
}
if (courseID != null)
{
ViewBag.CourseID = courseID.Value;
// Lazy loading
//viewModel.Enrollments = viewModel.Courses.Where(
// x => x.CourseID == courseID).Single().Enrollments;
// Explicit loading
var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
db.Entry(selectedCourse).Collection(x => x.Enrollments).Load();
foreach (Enrollment enrollment in selectedCourse.Enrollments)
{
db.Entry(enrollment).Reference(x => x.Student).Load();
}
viewModel.Enrollments = selectedCourse.Enrollments;
}
return View(viewModel);
}
選択したエンティティを取得した Course
後、新しいコードはそのコースの Enrollments
ナビゲーション プロパティを明示的に読み込みます。
db.Entry(selectedCourse).Collection(x => x.Enrollments).Load();
次に、各 Enrollment
エンティティの関連 Student
エンティティを明示的に読み込みます。
db.Entry(enrollment).Reference(x => x.Student).Load();
コレクション プロパティを読み込むには メソッド Collection
を使用しますが、1 つのエンティティのみを保持するプロパティの場合は、 メソッドを Reference
使用します。
[Instructor Index]\(インストラクター インデックス\) ページを今すぐ実行すると、データの取得方法を変更しましたが、ページに表示される内容に違いはありません。
コードを取得する
その他のリソース
他の Entity Framework リソースへのリンクは、「 ASP.NET データ アクセス - 推奨リソース」にあります。
次の手順
このチュートリアルでは、次の作業を行いました。
- 関連データを読み込む方法を学習した
- Courses ページを作成した
- Instructors ページを作成した
関連データを更新する方法について学習するには、次の記事に進んでください。
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示