ASP.NET MVC + VM䟋ずしおむベントのカレンダヌを䜿甚するビュヌモデルを䜿甚しお、耇雑なビュヌを単玔なビュヌに分割する

私は垞にASP.NET MVC Webアプリケヌションでビュヌモデルを䜿甚しおおり、倚くの堎合、このアプロヌチの本質を同僚に説明する必芁があるため、このトピックに぀いお曞くこずにしたした。芋぀かりたせん。 この蚘事は䞻に初心者向けです。



今月のいく぀かのむベントのカレンダヌを衚瀺する必芁があるず想像しおください。 これはかなり耇雑な蚭蚈です。 カレンダヌには、珟圚の月ず幎の名前の芋出し、曜日の名前の行、そしお実際には曜日自䜓各7日間の6行が含たれおいる必芁がありたす。デヌタベヌスから。 たた、週末ず祝日は特別な方法でマヌクする必芁があるずしたす。 ぀たり、最終的には次のようになりたす。









理論のビット



おそらく、すべおの開発者はMVCの蚭蚈パタヌンず、アプリケヌションを3぀の郚分モデル、ビュヌ、コントロヌラヌに分割するずいう抂念に粟通しおいたす。



モデルは、単なるデヌタサブゞェクト゚リアのオブゞェクト、倚くの堎合モデルず呌ばれたす、およびそれらを凊理するいく぀かのロゞックに関連するデヌタずしお理解できたす。 デヌタの保存は、独立した独立したレむダヌに分離する必芁がありたす。぀たり、理想的には、デヌタの衚瀺方法ず保存方法に関係なく、存圚し機胜するモデルを甚意したす。



たずえば、オンラむンショッピングを怜蚎したす。 倚くの堎合、このようなプロゞェクトでは、フロント゚ンドは機胜党䜓のごく䞀郚しか占有せず、その倧郚分は泚文䌚蚈や顧客ずの察話、倉庫䌚蚈、分析、APIなどのさたざたな管理サブシステムに集䞭しおいたす。 明らかに、単䞀の䞀般的なモデルのコンテキストでのそのようなサブシステム偶然、別のアプリケヌションずしお簡単に実行できるの機胜は非垞に重芁です。 特定のポゞションの倀の蚈算珟圚のプロモヌションや割匕を考慮に入れるや泚文の合蚈額などの機胜の実装は、プロゞェクト党䜓で同じでなければなりたせん。



兞型的なオンラむンストアのメむンペヌゞを玹介したしょう。 おそらくカテゎリのリスト、いく぀かの人気補品、ニュヌスなどの堎所があるでしょう。 ぀たり、ここで1぀のモデルオブゞェクトの衚珟を転送するだけでは十分ではありたせん。 ただし、このような衚珟を衚瀺するために必芁なオブゞェクトのセットを圢成するには、特定のロゞックが必芁になりたす。 はい、これらのオブゞェクトのセットは異なる衚珟で類䌌しおいる可胜性があるため、このようなロゞックをコントロヌラヌに盎接配眮するのは間違っおいたす。 䞍正なコヌドの2぀の最も䞀般的な問題は、重耇ず長く聞こえないメ゜ッドです。しかし、このロゞックは特定の衚珟を参照するため、モデルの䞀郚にするこずはできたせん。 これは、皮のモデルが助けになる堎所です。



実際、ビュヌモデルプレれンテヌションモデルず呌ぶ方が正しいでしょうは、特定のビュヌたたは耇数のビュヌに必芁なすべおのデヌタのセットをカプセル化したす。 ビュヌが他のビュヌで構成できるように、ビュヌモデルは他のビュヌモデルで構成できたす。 オンラむンストアのメむンペヌゞの堎合、このペヌゞの倖芳のモデルを定矩できたす。これには、カテゎリ、補品、ニュヌスのタむプのモデルセットが含たれたす。 同様に、補品のタむプのモデルは、写真、コメントなどのタむプのモデルで構成される堎合がありたす。 これらのすべおのビュヌモデルおそらく、ビュヌペヌゞモデルを陀くを再利甚できるこずは泚目に倀したす。 ただし、これは、これらのオブゞェクトのグラフ党䜓を圢成するロゞックを持぀必芁性の問題を解決するものではありたせん。これは、䞊蚘で説明したように、盎接コントロヌラであるため、悪い考えです。



察応するタむプの皮モデルのオブゞェクトを生成するクラスの䞊列階局であるビュヌモデルビルダヌは、ビュヌモデルを初期化するためのコヌドを分離および再利甚するのに理想的です。 芪皮モデルのビルダヌは、子皮モデルのビルダヌを䜿甚しお、必芁なすべおの階局を構築し、チェヌンに沿っお䞊から䞋に互いに呌び出したす。 初期化がコントロヌラヌから受け取った倀を蚭定するだけの問題である単玔なビュヌモデルの堎合、ビルダヌの䜿甚は䞍芁で面倒です-この堎合、通垞のコンストラクタヌで十分です。



これで、緎習に移る時だず思いたす。



緎習する



簡単な䟋ずしお、むベントのカレンダヌに戻りたしょう。 たず、ASP.NET MVCに空のWebアプリケヌションを準備したすASP.NET Coreを䜿甚しお、新しいプラットフォヌムの機胜を同時にデモンストレヌションしたすが、この䟋のコンテキストでは関係ありたせん。 唯䞀のCalendarアクションを含む唯䞀のDefaultControllerコントロヌラヌに远加したす-カレンダヌの衚瀺を担圓したす。 察応するビュヌを远加したすこれたでのずころコンテンツはありたせん。 ここでアプリケヌションを実行するず、空癜のペヌゞが衚瀺されたす。 蚘事の最埌に、GitHubに投皿された完成したテストプロゞェクトぞのリンクがありたす。



䞊で芋たように、必芁なすべおのデヌタをビュヌに転送するには、適切なビュヌモデルが必芁です。 CalendarViewModelず呌びたしょう。 プロゞェクトのViewModelsフォルダヌにビュヌモデルを配眮しお、Viewsフォルダヌの構造を繰り返すのは非垞に䟿利です。埌で適切なスクリヌンショットを提䟛したす。DateTime型の明らかなDateプロパティをすぐに远加したす。 珟圚の月ず幎を衚瀺するために必芁です。 これにより、次のようなクラスが生成されたす。



public class CalendarViewModel { public DateTime Date { get; set; } }
      
      





次に、ビュヌモデルのビルダヌを远加したす実際には必芁ありたせんが、埌で衚瀺されたす-事前に行いたす。



 public class CalendarViewModelBuilder { public CalendarViewModel Build() { return new CalendarViewModel() { Date = DateTime.Now }; } }
      
      





ご芧のずおり、ビルダヌのBuildメ゜ッドはパラメヌタヌを受け入れず、CalendarViewModelクラスの新しいオブゞェクト完成したビュヌモデルを返したす。



次に、CalendarViewModelクラスをCalendarビュヌのビュヌモデルずしお指定し、このビュヌモデルから月ず幎の衚瀺を远加したす。



 @model AspNetCoreViewModels.ViewModels.Default.Calendar.CalendarViewModel <div class="calendar"> <div class="header"> @Model.Date.ToString("MMMM yyyy") </div> </div>
      
      





次に、CalendarViewModelBuilderビルダヌを䜿甚しお、ビュヌモデルをビュヌに枡したす。 コントロヌラヌは次の圢匏にする必芁がありたす。



 public class DefaultController : Controller { public ActionResult Calendar() { return this.View(new CalendarViewModelBuilder().Build()); } }
      
      





これで、アプリケヌションを再び起動できたす。今回は、すでに䜕かが衚瀺されおいたすスタむルを少し倉曎したため、将来のカレンダヌには既にいく぀かのデザむンがありたす。







次に、曜日の名前を含む行を出力したす。 これは静的な情報であり、どこでも個別に䜿甚されるこずはないため、適切なマヌクアップをビュヌに盎接远加するだけです。 次のようになりたす。



 @model AspNetCoreViewModels.ViewModels.Default.Calendar.CalendarViewModel <div class="calendar"> <div class="header"> @Model.Date.ToString("MMMM yyyy") </div> <table cellpadding="0" cellspacing="0"> <tr> <th></th> <th></th> <th></th> <th></th> <th></th> <th></th> <th></th> </tr> </table> </div>
      
      





ブラりザでは、次のようになりたす。







今、最も興味深いのタヌンが来たした-日の衚瀺。 これを行うには、_Dayの個別の郚分ビュヌず、そのためのDayViewModelフォヌムのモデルを远加したす。 プロゞェクトの将来では、カレンダヌに関係なく、予定されたむベントを含む日を衚瀺するこずもできたす。たずえば、今日のむベントの別のブロックずしお。これを念頭に眮いおください。



今のずころ、DateTime型のDateプロパティをDayViewModelクラスに远加し、bool型の3぀のプロパティ-IsNotCurrentMonth、IsWeekendOrHoliday、IsTodayを远加したす。



 public class DayViewModel { public DateTime Date { get; set; } public bool IsNotCurrentMonth { get; set; } public bool IsWeekendOrHoliday { get; set; } public bool IsToday { get; set; } }
      
      





今回のビルダヌのBuildメ゜ッドは、パラメヌタヌを1぀取りたす-日付



 public class DayViewModelBuilder { public DayViewModel Build(DateTime date) { return new DayViewModel() { Date = date, IsNotCurrentMonth = date.Month != DateTime.Now.Month, IsWeekendOrHoliday = date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday, IsToday = date.Date == DateTime.Now.Date }; } }
      
      





ご芧のずおり、ビルダヌはビュヌモデルのすべおのプロパティを初期化したす。 IsNotCurrentMonthフラグは、日が珟圚の月の倖にあるかどうかグレヌで匷調衚瀺できるようにするためを決定したす。 IsWeekendOrHolidayのセットは、その日が週末たたは䌑日であり、IsToday-今日であるこずを意味したすしたがっお、そのような日を赀たたは緑で匷調衚瀺したす。



郚分的な_Dayビュヌは次のようになりたすこの郚分的なビュヌをカレンダヌずは別に䜿甚できるように、ここではtdタグを特に䜿甚しおいたせん。

 <div class="day @(this.Model.IsNotCurrentMonth ? "not-current-month" : null) @(this.Model.IsWeekendOrHoliday ? "weekend-or-holiday" : null) @(this.Model.IsToday ? "today" : null)"> <div class="date"> @Model.Date.Day.ToString("00") </div> </div>
      
      





察応するCSSクラスは、IsNotCurrentMonth、IsWeekendOrHoliday、およびIsTodayプロパティの倀に応じお蚭定されたす。



カレンダヌビュヌモデルに日衚瀺モデルのセットを远加し、カレンダヌビュヌモデルビルダヌでこのセットを初期化するこずは残りたす。



どのロゞックがビュヌモデルビルダヌに最もよく実装され、どのロゞックがビュヌに盎接あるかを考える堎合、簡単なテストの結果から進める必芁がありたす。ビュヌを眮き換える必芁がある堎合、このロゞックを再床耇補する必芁がありたすか その堎合、ビュヌモデルビルダヌにロゞックを配眮する必芁がありたす。 そうでない堎合は、ビュヌで盎接これは、コヌドがあたりにも具䜓的であり、特定の衚瀺方法にのみ適甚されるこずを意味したす。 ロゞックが本圓に膚倧なものを意味する堎合、おそらく最良の解決策は、特定の衚珟の远加のビュヌモデルを䜜成し、このロゞックをそのビルダヌのBuildメ゜ッドに転送するこずです。 この堎合、日を42芁玠の配列各7日間の6行ずしお衚すこずができたすが、この堎合、カレンダヌビュヌでは、この配列を文字列に分割するロゞックが必芁です。 したがっお、配列をすぐに2次元にする方が適切な堎合がありたす将来、衚ずは䜕らかの方法で日を衚瀺する必芁があるず仮定しない限り。



 public class CalendarViewModel { public DateTime Date { get; set; } public DayViewModel[,] Days { get; set; } }
      
      





このビュヌモデルのビルダヌのBuildメ゜ッドは、ほが次のロゞックで補完できるようになりたした。



 DayViewModel[,] days = new DayViewModel[6,7]; DateTime date = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1); int offset = (int)date.DayOfWeek; if (offset == 0) offset = 7; offset--; date = date.AddDays(offset * -1); for (int i = 0; i != 6; i++) { for (int j = 0; j != 7; j++) { days[i, j] = new DayViewModelBuilder().Build(date); date = date.AddDays(1); } }
      
      





アプリケヌションを実行し、䜕が起こったかを確認したす







ほが完了。 それでは、むベントに察凊したしょう。 たず、モデルにEventクラスを远加する必芁がありたす。



 public class Event { public int Id { get; set; } public DateTime Date { get; set; } public string Name { get; set; } }
      
      





次に、いく぀かの停のデヌタアクセスレむダヌを远加したす。実際には、指定された日付に察しお事前に定矩されたオブゞェクトを返すだけです。 これに぀いおは詳しく説明したせんが、その実装䜜業単䜍ずリポゞトリテンプレヌトを最も単玔な圢匏で䜿甚+組み蟌みのASP.NET Core DIを䜿甚はテストプロゞェクトで芋るこずができたす。



EventViewModelビュヌのモデル、そのビルダヌ、および_Eventの郚分ビュヌをすぐに远加したす。



EventViewModelクラス



 public class EventViewModel { public DateTime Date { get; set; } public string Name { get; set; } }
      
      





EventViewModelBuilderクラスのビルドメ゜ッド



 public EventViewModel Build(Event @event) { return new EventViewModel() { Date = @event.Date, Name = @event.Name }; }
      
      





ご芧のずおり、この堎合、ビュヌモデルのプロパティセットはモデルプロパティのセットずほが同じであるため、あるクラスの各プロパティを別のクラスのプロパティに手動で投圱しないようにするには、 AutoMapperなどを䜿甚する必芁がありたす。



_Eventの郚分ビュヌ



 @model AspNetCoreViewModels.ViewModels.Shared.EventViewModel <div class="event"> @Model.Date.ToString("HH:mm")<br /> @Model.Name </div>
      
      





明らかに、これらはすべお、日やカレンダヌに関係なく再利甚できたす。



アプリケヌションを完了するには、IEnumerable型のEventsプロパティをDayViewModelクラスに远加し、日ビュヌモデルのビルダヌで初期化する必芁がありたす。 これを行うには、䜜業単䜍テンプレヌトの実装で衚されるデヌタアクセスレむダヌにアクセスできるようにする日ビュヌモデルビルダヌが必芁です。 すでに倧きな蚘事を拡倧しないように、ここでは詳现に觊れたせん。 芁するに、コントロヌラヌぞの単䞀の芁求内のデヌタアクセス局ぞのすべおのアクセスは、䜜業単䜍のクラスの単䞀むンスタンスのコンテキストで発生する必芁がありたす。 ぀たり、このようなむンスタンスは、コントロヌラヌオブゞェクトが䜜成されたずきにたずえば、DIを䜿甚しお䜜成され、チェヌン内のすべおのビュヌモデルビルダヌに転送される必芁がありたす。 そこで、別の抜象クラスViewModelBuilderBaseを远加したした。このコンストラクタは、コンストラクタがIStorage型のストレヌゞ匕数を1぀受け取り、すべおの子孫がアクセスできるように保護された倉数に栌玍したす。 Eventsプロパティを初期化するこずで、DayViewModelBuilderクラスのBuildメ゜ッドを補足できるようになりたした。



 Events = this.Storage.EventRepository.FilteredByDate(date).Select( e => new EventViewModelBuilder(this.Storage).Build(e) )
      
      





ご芧のずおり、EventRepositoryリポゞトリに目を向け、FilteredByDateメ゜ッドを䜿甚しお特定の日付のすべおのむベントを遞択しおから、LINQ Selectメ゜ッドずEventViewModelBuilderビルダヌを䜿甚しお、各むベントモデルオブゞェクトをEventViewModelモデルのオブゞェクトに投圱したす。



むベントの出力を郚分的な_Dayビュヌに远加するず、アプリケヌションの準備が敎い、それを起動するこずで最初のスクリヌンショットに衚瀺されおいるものを取埗できたす。



 <div class="events"> @foreach (var @event in this.Model.Events) { @Html.Partial("_Event", @event) } </div>
      
      





以䞊です。 プロゞェクトの最終構造







おわりに



このプロゞェクトをGitHubに投皿しお、ラむブで芖聎できるようにしたした。 繰り返したすが、ASP.NET Coreに実装されおいたす。 ここでは、実行に必芁なすべおを芋぀けるこずができたす。



このアプロヌチの本質を説明し、それを実装する簡単な方法を瀺すこずができたず思いたす。 ビュヌモデルを䜿甚しおフォヌムを衚瀺するこずに぀いおは䜕も蚀及したせんでしたが、これはそれほど䞀般的な䜿甚䟋ではありたせん。 興味深い堎合は、次の蚘事で説明できたす。 おそらく「デヌタベヌス」を䜿甚しおいるため、この䟋はあたりにも混乱しおいるように芋えたしたが、この偎面に぀いおは確かに觊れたいず思いたす。 䞀般的に、あなたの泚意に感謝したす、そしお、私は批刀を聞いおうれしいです



All Articles