【T8a】多対多のリレーションシップ(8/8) プロジェクトタイプ (注意: 本文参照) プロジェクト名 T8aソリューション名 PIT8
注意 本ページの作業内容は 前のページまでの続き  になっていることに注意せよ.先に前のページまでをすべて読み,指示されている作業を済ませてから本ページを読むこと. プロジェクトの作成作業については準備作業 を参照せよ.  8a-8. 「サークル所属」エンティティの追加 さて話を元に戻そう.ここでは多対多のリレーションシップの作成を試みていたのであった.
ここまでで,図8a-5-2 で示したエンティティのうち「学生」と「サークル」を作成することができた.
つぎはこれらのあいだの関係を表すエンティティである「サークル所属」を表すモデルクラスを作成してみよう.
この「サークル所属」の情報には_ に示す項目を含めることにしよう.
「サークル所属」の情報 項目名 必須/任意 データ型 備考 学生ID 必須 整数 主キーの一部.「学生」への外部キー. サークルID 必須 整数 主キーの一部.「サークル」への外部キー. 
ポイントは,「学生」と「サークル」への外部キーだけを含んでおり,かつこの「学生」と「サークル」のIDの
組み合わせ自体が主キーとなっている点  である.このような複数の列の組み合わせからなる主キーを コンポジットキー  (複合主キー)という.
コンポジットキーを用いる理由は,例を使って説明した方が分かりやすいだろう.
たとえば「学生」と「サークル」それぞれに_ に示すようなデータが格納されているとしよう.
「学生」と「サークル」のレコード例 これに対してエンティティ「サークル所属」は,「 誰がどのサークルに所属するか  」を個々のレコードで表現する.
たとえば「サークル所属」は_ のような内容を含む.
「サークル所属」のレコード例1 このデータは_ に示す所属状況を意味している.すなわち,学生ID:1の「医療太郎」さんは2つのサークル,
「テニスサークル(ID:1)」と「軽音サークル(ID:2)」に所属しており,また,学生ID:4の「山田奈緒子」さんは「漫画研究会(ID:3)」に,
学生ID:5の「山田太郎」さんは「テニスサークル(ID:1)」にそれぞれ所属している.
学生IDとサークルIDの組が主キーになっているのは,ある学生があるサークルに所属しているのであれば「サークル所属」の
レコードの組み合わせは一通りしかありえないためである.たとえば_ のように
「誰がどのサークルに所属するか」の情報が重複して登録されることはあり得ない.
「サークル所属」のレコード例2 このため学生IDとサークルIDの組を主キーに位置付けているわけである.
それではこの「サークル所属」に対応するモデルクラスを定義しよう.クラス名は「サークル所属」をそのまま
英訳してCircleAffiliationとする.プロジェクト内の Models フォルダを右クリックし,「追加」→「クラス」をクリックする.
作成するクラス名を訊かれるのでCircleAffiliation(.csは省略可能)と入力して「追加」ボタンをクリックする.
すると空のクラス定義が作られるので_ の定義を書き込もう.
「サークル所属」クラス  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 using  Microsoft.EntityFrameworkCore ; 
using  System.ComponentModel.DataAnnotations ; 
 namespace  T8a.Models 
{ 
    // 「サークル所属」クラス 
     [PrimaryKey(nameof(StudentId), nameof(CircleId))]  // コンポジットキーの指定 
    public  class  CircleAffiliation 
     { 
         [Display(Name = "学生ID")] 
        public  int  StudentId  {  get ;  set ;  }     // 「学生」への外部キープロパティ 
         public  Student ?  Student  {  get ;  set ;  }  // ↑のための参照ナビゲーションプロパティ 
 
         [Display(Name = "サークルID")] 
        public  int  CircleId  {  get ;  set ;  }      // 「サークル」への外部キープロパティ 
         public  Circle ?  Circle  {  get ;  set ;  }    // ↑のための参照ナビゲーションプロパティ 
     } 
 } 
7行目でクラス定義に付与されている[PrimaryKey]属性 は,
このクラスの主キーがStudentIdとCircleIdのコンポジットキーであることを指定するものである.この属性には
コンポジットキーを構成するプロパティ名を文字列型の値として列挙する.ここではnameof式 を
使って,StudentIdプロパティとCircleIdプロパティの名前を文字列として取り出して利用している.コンポジットキーはこのように指定するということを覚えておこう.
次に, Data/T8aContext.cs に_ に示す内容を追記する.
T8aContextクラスへの追記内容  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 using  Microsoft.EntityFrameworkCore ; 
using  T8a.Models ; 
 namespace  T8a.Data 
{ 
    public  class  T8aContext  :  DbContext 
     { 
         public  T8aContext ( DbContextOptions < T8aContext >  contextOptions ) 
             :  base ( contextOptions ) 
         {  } 
 
 
         public  DbSet < Student >  Students  =>  Set < Student >(); 
 
         public  DbSet < Department >  Departments  =>  Set < Department >(); 
 
         public  DbSet < Circle >  Circles  =>  Set < Circle >(); 
 
         public  DbSet < CircleAffiliation >  CircleAffiliations  =>  Set < CircleAffiliation >();  // 追加 
 
         protected  override  void  OnModelCreating ( ModelBuilder  modelBuilder ) 
         { 
             base . OnModelCreating ( modelBuilder ); 
 
             modelBuilder . Entity < Department >(). HasData ( 
                 new  Department ()  {  Id  =  1 ,  Name  =  "医療情報学科"  }, 
                 new  Department ()  {  Id  =  2 ,  Name  =  "医療栄養学科"  }, 
                 new  Department ()  {  Id  =  3 ,  Name  =  "医療看護学科"  } 
                 ); 
         } 
     } 
 } 
19行目では「サークル所属」をデータベース上のテーブルとして反映させるためにDbSet<TEntity>型のプロパティを追加している.
ここまで書けたらマイグレーションコードを生成して,データベースにこの変更を反映させてみよう.
コマンドラインターミナル_ に示すコマンドを実行する(_ ).
マイグレーション処理の生成と適用 
PS> dotnet ef migrations add AddCircleAffiliation
PS> dotnet ef database update
 実行したら pgAdmin でデータベースの変化も確認しておこう. pgAdmin を起動するか,もしくは pgAdmin がすでに起動中の場合は
「 Servers 」→「 PostgreSQL 16 」→「 Databases 」を右クリックして「 Refresh 」を実行する.
「 Databases 」→「 t8a_db 」→「 Schemas 」→「 public 」→「 Tables 」に,新たなテーブル「 CircleAffiliations 」が
追加されていることを確認する(_ ).また「 CircleAffiliations 」を右クリックして「 Properties 」をクリックし,
このテーブルの列の情報( Columns タブ)と主キー制約と外部キー制約の情報( Constrains タブの Primary Key と Foreign Key )を
確認しておこう(_ ~_ ).
マイグレーション処理の結果を確認 「Constrains」タブの「Primary Key」には,PK_CircleAffiliationsという名前の制約が作られており,その内容が StudentId,CircleId となっており
コンポジットキーが構成されていることがわかるだろう(_ ).また「Constrains」タブの「Foreign Key」には,
「 Circles 」テーブルを参照する外部キーと,「 Students 」テーブルを参照する外部キー制約が表示されていることが分かるだろう(_ ).
前述の説明の通りこの「サークル所属」のエンティティは,「学生」や「サークル」のエンティティから見れば「多」の側,つまり従属エンティティである.
C#のコード上で,「学生」からその学生が所属しているサークルの情報を手繰り寄せたり,「サークル」からそのサークルに所属している学生の情報を手繰り寄せたり
するためにはコレクションナビゲーションプロパティが必要である. Models/Student.cs と Models/Circle.cs にそれぞれ
_ ,_ に示す内容を追記しよう.
Studentクラスへの追記内容  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 // 「学生」クラス 
public  class  Student 
{ 
    [Display(Name = "ID")] 
    public  int  Id  {  get ;  set ;  }           // ID 
 
     [Display(Name = "姓")] 
    public  string  LastName  {  get ;  set ;  }  =  "" ;    // 姓 
 
     [Display(Name = "ミドルネーム")] 
    public  string?  MiddleName  {  get ;  set ;  }        // ミドルネーム 
 
     [Display(Name = "名")] 
    public  string  FirstName  {  get ;  set ;  }  =  "" ;   // 名 
 
     [Display(Name = "性別")] 
    public  SexType  Sex  {  get ;  set ;  }          // 性別 
 
     [Phone] 
    [Display(Name = "電話番号")] 
    public  string  PhoneNumber  {  get ;  set ;  }  =  "" ; // 電話番号 
 
     [EmailAddress] 
    [Display(Name = "メールアドレス")] 
    public  string?  Mail  {  get ;  set ;  }         // メールアドレス 
 
     [DataType(DataType.Date)] 
    [Display(Name = "誕生日")] 
    public  DateTime ?  Birthday  {  get ;  set ;  }     // 誕生日 
 
     [DataType(DataType.DateTime)] 
    [Display(Name = "登録日時")] 
    public  DateTime  Registered  {  get ;  set ;  }   // 登録日時 
 
     [Display(Name = "所属学科")] 
    public  int  DepartmentId  {  get ;  set ;  } 
 
     public  Department ?  Department  {  get ;  set ;  } 
 
     // コレクションナビゲーションプロパティ 
     public  List < CircleAffiliation >?  CircleAffiliations  {  get ;  set ;  } 
 } 
Circleクラスへの追記内容  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 // 「サークル」クラス 
public  class  Circle 
{ 
    [Display(Name = "ID")] 
    public  int  Id  {  get ;  set ;  } 
 
     [Display(Name = "サークル名")] 
    public  string  Name  {  get ;  set ;  }  =  "" ; 
 
     [Display(Name = "サークル説明")] 
    public  string?  Description  {  get ;  set ;  } 
 
     // コレクションナビゲーションプロパティ 
     public  List < CircleAffiliation >?  CircleAffiliations  {  get ;  set ;  } 
 } 
次にこの「サークル所属」のレコードを操作するためのアクションやビューを作成しよう.
今回は「サークル所属」のための専用のコントローラーを作成するのでは なく  ,Studentsコントローラーに
所属サークルの情報を編集するためのアクションEditCircleAffiliations(GET/POST用)を追加することにする(_ ).
「サークル所属」のための画面イメージと画面遷移 EditCircleAffiliationsアクションでは,すべてのサークルをチェックボックスのリストとして表示する.その学生がサークルに
所属している場合にはチェックボックスにチェックが入っている状態にし,未所属の場合にはチェックが入っていない状態で表示する.
このような所属に関する情報を編集するためのUIのパターンはほかにもありうるが,
今回はサークル数がそれほど多くはならない(サークル数≪学生数)と考えられるため,存在する全サークルをこのような方法で表示・編集することにする.
この表示・編集のためには このビューでのみ使用する専用の小さなモデルクラス  を用意するのが最もシンプルな方法である.
この小さなクラスには, 「サークル」と「そのサークルに所属してるかいないか」  という情報を含め,このクラスのリストを
ビューとアクションのあいだでやり取りする.このクラスはあくまでもデータのやり取りのためのクラスなのでデータベースに
反映させるエンティティではないことに注意せよ.
それでは実際に作成してみよう.プロジェクト内の Models フォルダを右クリックし,「追加」→「クラス」をクリックする.
作成するクラス名を訊かれるのでCircleSelection(.csは省略可能)と入力して「追加」ボタンをクリックする.
すると空のクラス定義が作られるので_ の定義を書き込もう.
CircleSelectionクラス  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 namespace  T8a.Models 
{ 
    // 「サークル」と「そのサークルに所属してるかいないか」の情報のためのクラス 
     public  class  CircleSelection 
     { 
         public  int  CircleId  {  get ;  set ;  }             // サークルID 
         public  string  CircleName  {  get ;  set ;  }  =  "" ;  // サークル名 
 
         public  bool  Selected  {  get ;  set ;  }            // 所属(true) or 未所属(false) 
     } 
 } 
このクラスのコレクションを作成するには,リレーショナルデータベースの用語でいうところの 外部結合  が必要になる.
「サークル所属」から特定の学生の所属サークルを拾うだけでは,当然のことながらその学生の所属しているサークルしかリスト化することができない.
_ に示す通りEditCircleAffiliationsアクションのビューでは,所属しているかしていないかにかかわらず
全サークルをチェックボックスのリストとして表示するため,「サークル」と「サークル所属」を外部結合することでCircleSelectionクラスの
コレクションを作成する必要がある(_ ).
「サークル」と「サークル所属」の外部結合 これを踏まえたうえで,まずGET用のEditCircleAffiliationsアクションメソッドを作成しよう.
Controllers/StudentsController.cs に_ に示す内容を追記する.
StudentsControllerクラスへの追記内容1  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 // EditCircleAffiliationsアクション(GET用) 
public  async  Task < IActionResult >  EditCircleAffiliations ( int?  id )  
{ 
    if  ( id  ==  null )                                                   // idが指定されなかったら 
         return  NotFound ();                                            // 404 Not Found 
 
     var  student  =  await  _context . Students . FindAsync ( id );              // idで指定された学生情報を探す 
 
     if  ( student  ==  null )                                              // idで指定された学生が 
         return  NotFound ();                                            // 見つからなかったら 404 Not Found 
 
     var  cs  =  ( from  c  in  _context . Circles                              // CircleとCircleAffiliationを外部結合する. 
                 join  ca  in  _context . CircleAffiliations                //  
                 on  new  {  CircleId  =  c . Id ,  StudentId  =  student . Id  }    // 結合条件はサークルIDが一致していてかつ, 
                     equals  new  {  ca . CircleId ,  ca . StudentId  }  into  gj  // ↑の学生のIDであること. 
                 from  joinedCA  in  gj . DefaultIfEmpty ()                  // 結合条件に合わないCirlceの場合はNULL 
                 orderby  c . Id  ascending                                // 
                 select  new  CircleSelection ()  {                        // 
                     CircleId  =  c . Id ,                                  // 
                     CircleName  =  c . Name ,                              // 
                     Selected  =  joinedCA  !=  null                       // joindCAがNULLかどうかをSelectedに入れる. 
                 });                                                   // 
 
     ViewBag . Student  =  student ;                                        // ビューでは学生自体の情報も必要なので 
                                                                      // .ViewBag に入れておく. 
 
     return  View ( await  cs . ToListAsync ()); 
 } 
_ のようにLINQでも join というキーワードを用いて,指定した条件で複数のモデルクラスを結合することができる.
LINQではこれをグループジョイン という用語で呼ぶ.
この場合はCirecleとCircleAffiliationをグループジョインしており,その結合条件は14~15行目のon ××× equals ○○○の部分である.
ここでは「サークル」のIDとルーティングパラメーターで指定された学生IDの組(×××の部分)が,
「サークル所属」のサークルIDと学生IDの組(○○○の部分)と等しいことを結合条件としている.
そしてこの結合条件に合致するCircleAffiliationオブジェクトのコレクションを範囲変数gjに代入している.
このクエリを外部結合にするためのポイントは16行目の.DefaultIfEmpty()である.これはグループジョインした被結合側のコレクションが(gj)が
空である場合はこれを null として扱う,言い換えると結合条件に合致するものがなくても結合結果に含めるという作用を持つ.
そして18~22行目の select 句で,これらの情報からCircleSelectionクラスのオブジェクトを生成している.
後述するこのアクションのビューでは,編集対象の学生の情報も表示するので,24行目ではルーティングパラメーターidで指定されたStudentオブジェクトをViewBagにセットしている.
またメソッドの最後の return文 では前述のLINQの結果をリスト化してビューに渡している.
次にこのアクションのためのビューを作成しよう.
Views/Students フォルダにEditCircleAffiliationsという名前のビューを追加する.
Views/Students/EditCircleAffiliations.cshtml を_ のように変更する.
Views/Students/EditCircleAffiliations.cshtmlの内容  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 @model List< CircleSelection > 
 
 @{
     ViewData["Title"] = "所属サークル編集";
 
     if (Model is null) throw new ArgumentNullException(nameof(Model)); // ad-hoc! 非null保証のための回避策
 }
 
 @* 編集対象の学生情報の表示 *@
 @{ var s = (Student)ViewBag.Student; }
 < p > ID:@s.Id, @s.LastName @s.FirstName</ p > 
 @* 所属サークルの編集のためのフォーム *@
 < form  asp-action = "EditCircleAffiliations" > 
    @for (int i = 0; i <  Model.Count ;  ++ i ) 
     { 
         < input  asp-for = "@Model[i].Selected"  /> 
         < input  type = "hidden"  asp-for = "@Model[i].CircleId"  />         
         < label  asp-for = "@Model[i].Selected" > @Model[i].CircleName</ label >         
         < br  /> 
     }
     < input  type = "submit"  value = "送信"  /> 
 </ form > 
 < a  asp-action = "Details"  asp-route-id = "@(s.Id)" > 学生詳細に戻る</ a > 
ここではあえて foreach ではなく for 文を用いてリストを反復しているが,これは必須である.
特定のデータのコレクションを送信させる場合は, asp-for タグヘルパーにインデックスが表れる形で記述する必要がある.
さらに学生詳細画面(Details)から,このビューへのリンクを貼るため Views/Students/Details.cshtml に_ に示す内容を追記する.
Views/Students/Details.cshtmlの追記内容  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 @model Student
 
 @{
     ViewData["Title"] = "学生詳細";
 
     if (Model is null) throw new ArgumentNullException(nameof(Model)); // ad-hoc! 非null保証のための回避策
 }
 
 < table > 
  < tr >< th > 項目</ th >< th > 値</ th ></ tr > 
 
   @* IDの表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.Id)</ td > 
      < td > @Html.DisplayFor(s => s.Id)</ td > 
   </ tr > 
 
   @* 姓(LastName)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.LastName)</ td > 
      < td > @Html.DisplayFor(s => s.LastName)</ td > 
   </ tr > 
 
   @* 名(FirstName)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.FirstName)</ td > 
      < td > @Html.DisplayFor(s => s.FirstName)</ td > 
   </ tr > 
 
   @* 性別(Sex)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.Sex)</ td > 
      < td > @Html.DisplayFor(s => s.Sex)</ td > 
   </ tr > 
 
   @* 所属学科(DepartmentId)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.DepartmentId)</ td > 
      < td > @Html.DisplayFor(s => s.Department.Name)</ td > 
   </ tr > 
 
   @* 電話番号(PhoneNumber)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.PhoneNumber)</ td > 
      < td > @Html.DisplayFor(s => s.PhoneNumber)</ td > 
   </ tr > 
 
   @* メールアドレス(Mail)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.Mail)</ td > 
      < td > @Html.DisplayFor(s => s.Mail)</ td > 
   </ tr > 
 
   @* 誕生日(Birthday)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.Birthday)</ td > 
      < td > @Html.DisplayFor(s => s.Birthday)</ td > 
   </ tr >   
   
   @* 登録日時(Registered)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.Registered)</ td > 
      < td > @Html.DisplayFor(s => s.Registered)</ td > 
   </ tr > 
 
   @* 所属サークル編集のためのリンク *@
   < tr > 
       < td > 所属サークル</ td > 
       < td > 
         < a  asp-action = "EditCircleAffiliations"  asp-route-id = "@Model.Id" > 編集</ a > 
       </ td > 
   </ tr > 
 
 </ table > 
 < a  asp-action = "Index" > 一覧に戻る</ a > 
| < a  asp-action = "Edit"  asp-route-id = "@Model.Id" > 編集</ a > 
 | < a  asp-action = "Delete"  asp-route-id = "@Model.Id" > 削除</ a >  
ここまで書けたら実行してみよう.「学生管理」のリンクをクリックして適当な学生の学生詳細画面(Details)を表示させ,
「所属サークル」の行の「編集」のリンクをクリックする(_ ~_ ).すると全サークルがチェックボックスのリストとして
表示されるはずである(_ ).いまのところこの送信を受け付けるためのアクションメソッドを作っていないので
まだ「送信」ボタンは機能しない  .
実行結果 つぎに,この送信を受け付けるためのアクションメソッドを作成しよう.
Controllers/StudentsController.cs に_ に示す内容を追記する.
StudentsControllerクラスへの追記内容2  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 // EditCircleAffiliationsアクション(POST用) 
[HttpPost] 
[ValidateAntiForgeryToken] 
public  async  Task < IActionResult >  EditCircleAffiliations ( int?  id ,  List < CircleSelection >  circleSelections ) 
{ 
    if  ( id  ==  null )                                            // idが指定されなかったら 
         return  NotFound ();                                     // 404 Not Found 
 
     var  student  =  await  _context . Students . FindAsync ( id );       // idで指定された学生情報を探す 
                                                               // 
     if  ( student  ==  null )                                       // idで指定された学生が 
         return  NotFound ();                                     // 見つからなかったら 404 Not Found 
                                                               
     if  ( ModelState . IsValid )                                    // 
     {                                                          // 
         foreach  ( var  cs  in  circleSelections )                   // 送信されたCircleSelectionごとに処理する 
         {                                                      // 
             var  sca  =  ( from  ca  in  _context . CircleAffiliations  // 指定された学生IDと, 
                         where  ca . StudentId  ==  student . Id       // CircleSelectionのサークルIDをもつ 
                             &&  ca . CircleId  ==  cs . CircleId      // CircleAffiliationのデータを探す. 
                         select  ca ). FirstOrDefault ();           // 
                                                               
             if  ( sca  !=  null  &&  ! cs . Selected )                   // 既存のサークル所属のチェックが解除された場合 
             {                                                  // 
                 _context . Remove ( sca );                          //   その所属情報を削除 
             }                                                  //      
             else  if  ( sca  ==  null  &&  cs . Selected )               // 新たなサークルにチェックが入れられた場合 
             {                                                  // 
                 _context . Add ( new  CircleAffiliation ()           //  
                 {                                              //   所属情報を作って追加する 
                     CircleId  =  cs . CircleId ,                    //  
                     StudentId  =  student . Id                     // 
                 });                                            // 
             }                                                  // 
         } //foreach                                            // 
 
         await  _context . SaveChangesAsync ();                     // データベースに変更を反映 
 
         return  RedirectToAction ( nameof ( Details ),  new  {  id  =  id  });  // Detailsに遷移 
     } 
     else 
     { 
         return  View ( circleSelections ); 
     } 
 } 
ここまで書けたら実行してみよう.「学生管理」のリンクをクリックして適当な学生の学生詳細画面(Details)を表示させ,
「所属サークル」の行の「編集」のリンクをクリックする(_ ,_ ).そして適当なサークルにチェックを入れて「送信」ボタンをクリックする(_ ).
すると学生詳細画面(Details)に戻るが,もう一度「所属サークル」の行の「編集」のリンクをクリックしてみよう.
先ほどチェックを入れたサークルがチェックされた状態で「所属サークル編集」の画面が表示されるはずである(_ ).
実行結果 このままでもよいが,学生詳細画面(Details)にも所属するサークルのリストが表示されるようにしてみよう.
Controllers/StudentsController.cs のDetailsメソッドに_ に示す内容を追記する.
StudentsControllerクラスへの追記内容3  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 // Detailsアクション(GETのみ) 
public  IActionResult  Details ( int?  id ) 
{ 
    if  ( id  ==  null ) 
         return  NotFound (); 
 
     var  student  =  ( from  s  in  _context . Students 
                     where  s . Id  ==  id 
                     select  s ) 
                     . Include ( s  =>  s . Department ) 
                     . FirstOrDefault (); 
 
     if  ( student  ==  null ) 
         return  NotFound (); 
 
     ViewBag . AffiliatedCircles  =  ( from  ca  in  _context . CircleAffiliations         // StudentIdが↑の学生IDに 
                                  where  ca . StudentId  ==  student . Id               // 一致するレコードをとりだして 
                                  orderby  ca . CircleId  ascending                  // リスト化して ViewBag に 
                                  select  ca ). Include ( ca  =>  ca . Circle ). ToList ();  // 格納する. 
 
     return  View ( student ); 
 } 
16~19行目ではLINQを使って,指定された学生と同じStudentIdをもつ「サークル所属」の
レコードを取り出してViewBagに格納している.つぎにビューを変更して格納したデータを表示するようにしてみよう.
Views/Students/Details.cshtml を_ に示す内容を追記する.
Views/Students/Details.cshtmlの追記内容  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 @model Student
 
 @{
     ViewData["Title"] = "学生詳細";
 
     if (Model is null) throw new ArgumentNullException(nameof(Model)); // ad-hoc! 非null保証のための回避策
 }
 
 < table > 
  < tr >< th > 項目</ th >< th > 値</ th ></ tr > 
 
   @* IDの表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.Id)</ td > 
      < td > @Html.DisplayFor(s => s.Id)</ td > 
   </ tr > 
 
   @* 姓(LastName)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.LastName)</ td > 
      < td > @Html.DisplayFor(s => s.LastName)</ td > 
   </ tr > 
 
   @* 名(FirstName)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.FirstName)</ td > 
      < td > @Html.DisplayFor(s => s.FirstName)</ td > 
   </ tr > 
 
   @* 性別(Sex)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.Sex)</ td > 
      < td > @Html.DisplayFor(s => s.Sex)</ td > 
   </ tr > 
 
   @* 所属学科(DepartmentId)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.DepartmentId)</ td > 
      < td > @Html.DisplayFor(s => s.Department.Name)</ td > 
   </ tr > 
 
   @* 電話番号(PhoneNumber)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.PhoneNumber)</ td > 
      < td > @Html.DisplayFor(s => s.PhoneNumber)</ td > 
   </ tr > 
 
   @* メールアドレス(Mail)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.Mail)</ td > 
      < td > @Html.DisplayFor(s => s.Mail)</ td > 
   </ tr > 
 
   @* 誕生日(Birthday)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.Birthday)</ td > 
      < td > @Html.DisplayFor(s => s.Birthday)</ td > 
   </ tr >   
   
   @* 登録日時(Registered)の表示 *@
   < tr > 
      < td > @Html.DisplayNameFor(s => s.Registered)</ td > 
      < td > @Html.DisplayFor(s => s.Registered)</ td > 
   </ tr > 
 
   @* 所属サークル編集のためのリンク *@
   < tr > 
       < td > 所属サークル</ td > 
       < td > 
         @{
             var affiliatedCircles = (List< CircleAffiliation > )ViewBag.AffiliatedCircles;
             if (affiliatedCircles.Count != 0)
             {
                 < ul > 
                     @foreach (var ca in affiliatedCircles)
                     {
                         < li > @ca.Circle?.Name</ li > 
                     }
                 </ ul > 
             }
         }
         < a  asp-action = "EditCircleAffiliations"  asp-route-id = "@Model.Id" > 編集</ a > 
       </ td > 
   </ tr > 
 
 </ table > 
 < a  asp-action = "Index" > 一覧に戻る</ a > 
| < a  asp-action = "Edit"  asp-route-id = "@Model.Id" > 編集</ a > 
 | < a  asp-action = "Delete"  asp-route-id = "@Model.Id" > 削除</ a >  
ここまで書けたら実行してみよう.「学生管理」のリンクをクリックして 先ほどサークルの所属情報を編集した 
学生の学生詳細画面(Details)を表示させてみよう.所属するサークルの情報が表示されるはずである(_ ,_ ).
また「所属サークル」の行の「編集」のリンクをクリックしてサークルの所属情報を編集してみよう.学生詳細画面(Details)に
戻ると編集結果が反映されているはずである(_ ,_ ).
実行結果 ここまでの動作を確認したら1つ目のチュートリアルは完了である.
次に進む前に,混乱を防ぐため Visual Studio のエディタをすべて閉じておこう.Visual Studio のいずれかのエディタのタブを右クリックして
「すべててのドキュメントを閉じる」をクリックすれば,エディタをすべて閉じることができる  .
Last updated on 2024-06-10