🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

ASP.NET

ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

ASP.NET MVC Framework

ASP.NET MVC Frameworkは、MVCパターンをベースとした、マイクロソフトのウェブアプリケーション開発用のフレームワークです。

Q&A

解決済

2回答

6465閲覧

ASP.NET Core MVCにおけるレコードの操作とその表示について

K-actus

総合スコア22

.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

ASP.NET

ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

ASP.NET MVC Framework

ASP.NET MVC Frameworkは、MVCパターンをベースとした、マイクロソフトのウェブアプリケーション開発用のフレームワークです。

0グッド

0クリップ

投稿2020/11/30 07:17

編集2020/12/01 04:30

こんにちは。
ASP.NET Coreについて初学者なので拙いところもあると思いますが、よろしくお願いします。

実現したいこと

※データベースのテーブルは「テーブル」、HTMLのテーブルについては"Table"とここでは表記します。

例として以下のようなModelのテーブルを想定します。

ID識別子名前
1Aりんご
2Aみかん
3Bパンダ
4Bキリン
5Aもも
6C日本

識別子はString型であり、種類数は決まっていません。

この時、識別子の種類分Tableを作成し、Viewで以下のように表示したいです。

ID識別子名前
1Aりんご
2Aみかん
5Aもも
ID識別子名前
3Bパンダ
4Bキリン
ID識別子名前
6C日本

わからないところ

識別子の数が決まっていないので、単なるSELECTだけでは実現出来ないと考えています。
今のところ、元のModelからデータを全て取ってきてForなりで識別子の種類数を算出し、その分ViewでTableを作成して割り振るといった処理を考えています。
しかしそれを実装するにしてもその処理をどこに記述するべきなのか(Controller? View?)わかっていません。
View側(cshtmlかjavascript)でデータの編集をしても良いのでしょうか?
それともModelに関することはController側で処理するのが好ましいですか?
またもっとスマートなやり方があればそうしたいです。
また、テーブルを分けるという方針は無しでお願いします。

よろしくお願いします。

補足情報

Visual Studio 2017 Community
Microsoft.AspNetCore.All 2.2.8
Windows10 Pro 1909

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/11/30 07:40 編集

> 識別子はString型であり、種類数は可変です。 「可変」というのはどういう意味ですか? 質問に書いてあるテーブルには A, B, C の 3 つしかありませんが、実際にはいくつあるかとその文字は DB に SELECT クエリ(例えば、SELECT DISTINCT [識別子] FROM [テーブル] というような)を投げて調べないと分からないということですか? 表示する Table を分ける必要があるのですか? ORDER BY [識別子] と使って [識別子] 順にレコードを並べて一つのテーブルに表示するのをお勧めします(分けるのはかなり面倒)。
K-actus

2020/11/30 07:36

そういうことです。 識別子がAだけでも、もしくはA~Zまであっても同じような動作を担保したいです。
退会済みユーザー

退会済みユーザー

2020/11/30 07:42 編集

表示する Table を分ける必要があるのですか? ORDER BY [識別子] を使って [識別子] 順にレコードを並べて一つの Table に表示するのをお勧めします(分けるのはかなり面倒)。
K-actus

2020/11/30 07:50

別々の表として扱いたいので分けたいです。 かなり面倒というのはどういった点に手間がかかりそうですか?
退会済みユーザー

退会済みユーザー

2020/11/30 08:18

> かなり面倒というのはどういった点に手間がかかりそうですか? 面倒さ難しさをイメージできませんか? 不可能とまでは言いませんが、かなりの無理筋です。そういうことにお構いなしの客の要求とかでない限り、ORDER BY [識別子] を使って [識別子] 順にレコードを並べて一つの Table に表示するのをお勧めします。
退会済みユーザー

退会済みユーザー

2020/11/30 22:44

上のコメント欄に書いた「可変」の意味の詳細を質問欄を編集して追記してください。 表示する Table を分けるのは客の要求とかで必須ということであれば、それも質問欄を編集して追記願います。そして、あなたがどこまで自分で実装できたか、どこで躓いていて、何が分かれば解決できるかもう少し具体的に質問欄に書いてください。
退会済みユーザー

退会済みユーザー

2020/12/01 04:45

表示する Table を分けるのは客の要求とかで必須ということであれば、それも質問欄を編集して追記願います。そして、あなたがどこまで自分で実装できたか、どこで躓いていて、何が分かれば解決できるかもう少し具体的に質問欄に書いてください。
K-actus

2020/12/01 09:24

種類数に応じて表示するテーブルを分けるのが出来そうになければ、識別子の上限を決めるとういう方針でも大丈夫です。 実装についてですが、具体的なロジック以前にこういった処理を実装する際デザインパターンとしてどうするべきかもわかっていないので、そこの部分についても教えていただきたいです。
退会済みユーザー

退会済みユーザー

2020/12/01 12:07

あなたがどこまで自分で実装できたか、どこで躓いていて、何が分かれば解決できるかもう少し具体的に質問欄に書いてください。
guest

回答2

0

ベストアンサー

識別子の数が決まっていないので、単なるSELECTだけでは実現出来ないと考えています。

今のところ、元のModelからデータを全て取ってきてForなりで識別子の種類数を算出し、その分ViewでTableを作成して割り振るといった処理を考えています。

LINQ to Entitiesの Queryable.GroupBy メソッドを使ってください。

Entity Frameworkを使用してデータベースからEntityのコレクションを取得できるように環境を整える必要があります。

※接続するプロバイダーによってはGroupByがSQL等の命令に変換できず、Client side GroupBy is not supported 等のエラーが出るため、その場合はEntityのコレクションを取得し、ToListメソッド等でIEnumerableに変換した上でLINQ to EntitiesではなくLINQ to ObjectsのGroupByを使ってください。

しかしそれを実装するにしてもその処理をどこに記述するべきなのか(Controller? View?)わかっていません。
View側(cshtmlかjavascript)でデータの編集をしても良いのでしょうか?

それともModelに関することはController側で処理するのが好ましいですか?

データの整形はViewでは行わないと思います。
となると、その選択肢の中ではControllerクラスに書くことになりますが、クリーンアーキテクチャのようなソフトウェアアーキテクチャを取り入れている場合はその限りではありません。

またもっとスマートなやり方があればそうしたいです。

率直に思いつくやり方は、EFとGroupByを使用した方法になります。

Controller

  • データベースからEntity Frameworkを使用してEntityのコレクションを取得
  • Controllerで識別子をキーにコレクションをGroupByし、その結果 IEnumerable<IGrouping<TKey,TElement>>)をViewに渡す

View

  • Razor構文を使用してforeachで各グループ毎にtableタグを使い表を作る
  • それぞれのtableタグでグループが持つ各要素毎にtr,thタグなどで行を追加&行にデータを書き込む

Sample

EFからスキャフォールディングで生成したControllerをベースに、Indexメソッドだけ弄ってます。

C#

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Threading.Tasks; 5using Microsoft.AspNetCore.Mvc; 6using Microsoft.AspNetCore.Mvc.Rendering; 7using Microsoft.EntityFrameworkCore; 8using WebApplication3.Models; 9 10namespace WebApplication3.Controllers 11{ 12 public class SampleController : Controller 13 { 14 private readonly SampleContext _context; 15 16 public SampleController(SampleContext context) 17 { 18 _context = context; 19 } 20 21 // GET: Sample 22 public async Task<IActionResult> Index() 23 { 24 return View(await _context.SampleDataModels.GroupBy(x => x.Identifier).ToListAsync()); 25 } 26 27 // GET: Sample/Details/5 28 public async Task<IActionResult> Details(int? id) 29 { 30 if (id == null) 31 { 32 return NotFound(); 33 } 34 35 var sampleDataModel = await _context.SampleDataModels 36 .FirstOrDefaultAsync(m => m.Id == id); 37 if (sampleDataModel == null) 38 { 39 return NotFound(); 40 } 41 42 return View(sampleDataModel); 43 } 44 45 // GET: Sample/Create 46 public IActionResult Create() 47 { 48 return View(); 49 } 50 51 // POST: Sample/Create 52 // To protect from overposting attacks, please enable the specific properties you want to bind to, for 53 // more details see http://go.microsoft.com/fwlink/?LinkId=317598. 54 [HttpPost] 55 [ValidateAntiForgeryToken] 56 public async Task<IActionResult> Create([Bind("Id,Identifier,Name")] SampleDataModel sampleDataModel) 57 { 58 if (ModelState.IsValid) 59 { 60 _context.Add(sampleDataModel); 61 await _context.SaveChangesAsync(); 62 return RedirectToAction(nameof(Index)); 63 } 64 return View(sampleDataModel); 65 } 66 67 // GET: Sample/Edit/5 68 public async Task<IActionResult> Edit(int? id) 69 { 70 if (id == null) 71 { 72 return NotFound(); 73 } 74 75 var sampleDataModel = await _context.SampleDataModels.FindAsync(id); 76 if (sampleDataModel == null) 77 { 78 return NotFound(); 79 } 80 return View(sampleDataModel); 81 } 82 83 // POST: Sample/Edit/5 84 // To protect from overposting attacks, please enable the specific properties you want to bind to, for 85 // more details see http://go.microsoft.com/fwlink/?LinkId=317598. 86 [HttpPost] 87 [ValidateAntiForgeryToken] 88 public async Task<IActionResult> Edit(int id, [Bind("Id,Identifier,Name")] SampleDataModel sampleDataModel) 89 { 90 if (id != sampleDataModel.Id) 91 { 92 return NotFound(); 93 } 94 95 if (ModelState.IsValid) 96 { 97 try 98 { 99 _context.Update(sampleDataModel); 100 await _context.SaveChangesAsync(); 101 } 102 catch (DbUpdateConcurrencyException) 103 { 104 if (!SampleDataModelExists(sampleDataModel.Id)) 105 { 106 return NotFound(); 107 } 108 else 109 { 110 throw; 111 } 112 } 113 return RedirectToAction(nameof(Index)); 114 } 115 return View(sampleDataModel); 116 } 117 118 // GET: Sample/Delete/5 119 public async Task<IActionResult> Delete(int? id) 120 { 121 if (id == null) 122 { 123 return NotFound(); 124 } 125 126 var sampleDataModel = await _context.SampleDataModels 127 .FirstOrDefaultAsync(m => m.Id == id); 128 if (sampleDataModel == null) 129 { 130 return NotFound(); 131 } 132 133 return View(sampleDataModel); 134 } 135 136 // POST: Sample/Delete/5 137 [HttpPost, ActionName("Delete")] 138 [ValidateAntiForgeryToken] 139 public async Task<IActionResult> DeleteConfirmed(int id) 140 { 141 var sampleDataModel = await _context.SampleDataModels.FindAsync(id); 142 _context.SampleDataModels.Remove(sampleDataModel); 143 await _context.SaveChangesAsync(); 144 return RedirectToAction(nameof(Index)); 145 } 146 147 private bool SampleDataModelExists(int id) 148 { 149 return _context.SampleDataModels.Any(e => e.Id == id); 150 } 151 } 152}

cshtml

1@model IEnumerable<IGrouping<string,WebApplication3.Models.SampleDataModel>> 2 3@{ 4 ViewData["Title"] = "Index"; 5} 6 7<h2>Index</h2> 8 9<p> 10 <a asp-action="Create">Create New</a> 11</p> 12 13@foreach (var group in Model) 14{ 15 <table class="table"> 16 <thead> 17 <tr> 18 <th> 19 @Html.DisplayNameFor(model => model.First().Identifier) 20 </th> 21 <th> 22 @Html.DisplayNameFor(model => model.First().Name) 23 </th> 24 <th></th> 25 </tr> 26 </thead> 27 <tbody> 28 @foreach (var item in group) 29 { 30 <tr> 31 <td> 32 @Html.DisplayFor(modelItem => item.Identifier) 33 </td> 34 <td> 35 @Html.DisplayFor(modelItem => item.Name) 36 </td> 37 <td> 38 <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> | 39 <a asp-action="Details" asp-route-id="@item.Id">Details</a> | 40 <a asp-action="Delete" asp-route-id="@item.Id">Delete</a> 41 </td> 42 </tr> 43 } 44 </tbody> 45 </table> 46}

イメージ説明

投稿2020/12/02 07:15

編集2020/12/03 06:49
BluOxy

総合スコア2663

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/12/03 00:14

Controller のサンプルコードを教えていただけませんか?
BluOxy

2020/12/03 01:09

ちょっと書いてみます。
BluOxy

2020/12/03 06:12 編集

補足にはなりますが、データの内容を見るとGroupByではなくFruit,Animal,Countryでテーブルを分けた方が多分良いと思います。 なぜなら、「果物」の成分や「日本」の国民の総人口をテーブルで抱えたいと思い至った時点でDB設計が破綻するからです。 「多分良い」と書いたのは、今後99.9999%そういった列が追加されないと断言できるのであれば今のテーブルでも問題ないと考えているからです。 とはいえ、それを断言できるケースは中々存在しないので、「テーブルを分ける方針はなし」と書いていますが、通常分けるべきと思います。 ※断言できるということは、すなわち未来に何が起きるか高い精度で想定できると考えている訳なので、中々強気な考えだと思います。DB設計による技術的負債は、解消するのがかなり大変(設計がデータ依存の場合は特に大変)なので気を付けてください。
BluOxy

2020/12/03 05:39

Controllerもその種類毎に定義されていれば、至極シンプルな実装になり皆幸せだと思います。
退会済みユーザー

退会済みユーザー

2020/12/03 05:58

サンプルをありがとうございました。
BluOxy

2020/12/03 06:02 編集

いえ、ASP.NET Core MVCは初めて触ったので少し勉強になりました。 (普段は .NET Framework 4.6 辺りに取り残されておりますので…) DIできる環境が最初から用意されている点が良かったです。
退会済みユーザー

退会済みユーザー

2020/12/03 06:17

GroupBy の使い方が勉強になりました。自分の回答のサンプルで試してみましたが期待通りの結果が得られました。おかげさまで一つ利口になりました。 ただ、自分の回答のサンプルでは以下のようにすると "Client side GroupBy is not supported." というエラーになりました。GroupBy は SQL に変換できないということのようです。 var model = await _context.Products.GroupBy(p => p.SupplierId).ToListAsync(); GroupBy は Linq to Entities ではなく、以下のように Linq to Objects で使えば問題なかったです。 List<Products> productList = await _context.Products.ToListAsync(); var model = productList.GroupBy(p => p.SupplierId); 自分の回答のサンプルで何故ダメなのかは分かりません(調査中)。
BluOxy

2020/12/03 06:44 編集

そういえば、こちらの環境ではDbContextをSQLサーバー接続ではなくIn-Memory データベースとして利用しています。(SQLサーバーをすぐに用意できなかったため)(もっと書けば準備が面倒臭かったので…) In-Memory データベースの厳密な仕組みはわかりませんが、SQLに変換するという過程が存在しないからこちらで事象が発生しないのだと思います。 その都合にControllerクラスの実装が影響すると想定できていませんでした。 (EFを使うときに気を付けるべきポイントかもしれません) 対処は SurferOnWww さんが行った方法で問題ないと思います。
退会済みユーザー

退会済みユーザー

2020/12/03 06:59

> こちらの環境ではDbContextをSQLサーバー接続ではなくIn-Memory データベースとして利用しています。 その違いが原因だったようですね。 ちなみに、 var model = await _context.Products.GroupBy(p => p.SupplierId).ToListAsync(); は "Client side GroupBy is not supported." というエラーですが、 var model = await _context.Products.GroupBy(p => p.SupplierId). Select(g => new { Id = g.Key, Total = g.Sum(g => g.UnitPrice) }). ToListAsync(); というのは SQL に変換できるようで、期待通りの結果が得られます。SQL 文で前者のようなデータを SQL Server から抽出してくるということはできないが(どういう SQL 文を書けばいいのだろう?)、後者のような ORDER BY を使うケースでよくある形なら当然 SQL 文に変換できるということなのかもしれませんね。 ご参考に以下の記事を紹介します。 LINQにも色々 ~SQLに変換されるモノと変換されないモノ https://codezine.jp/article/detail/8474
BluOxy

2020/12/03 08:07

本当はSQLの都合を意識しないで利用したい(インピーダンスミスマッチを感じたくない)ですが、現実的には難しいですね。参考記事ありがとうございます。
guest

0

質問のコメント欄で、

あなたがどこまで自分で実装できたか、どこで躓いていて、何が分かれば解決できるかもう少し具体的に質問欄に書いてください。

・・・とお願いしたのですが、返事がないということは多分手も足も出ないのであろうと想像して、[識別子] 別に複数の Table を表示する方法の案・ヒントを書いておきます。

Entitiy Framework とコンテキストクラス、エンティティクラスを使って DB からデータを取得していると想像してますが(それすら質問には書いてない情報不足ということを認識してください)、であれば List<List<T>> オブジェクト(T はエンティティクラス。[識別子] の数だけ List<T> を含む)を Controller で作って、それを View に渡し、View では foreach ループを回して [識別子] 別に Table を作成するようにしてはどうですか?

言葉では分かり難いと思いますので Controller のアクションメソッドのサンプルを書いておきます。エンティティクラス名は Products、[識別子] は Identifier に読み替えてください。 List<List<Products>> を作って View に渡します。

public async Task<IActionResult> ListByIdentifier() { List<Products> productList = await _context.Products.ToListAsync(); string[] identifiers = productList.Select(p => p.Identifier).Distinct().ToArray(); List<List<Products>> model = new List<List<Products>>(); foreach (string id in identifiers) { List<Products> productByIdentifier = productList.Where(p => p.Identifier == id).ToList(); model.Add(productByIdentifier); } return View(model); }

注:動かして検証などはしてない(DB がないとできない)頭の中で考えただけのサンプルです。質問者さんのケースでどのようにできるかは自分で考えてください。

【追記】

Microsoft が提供するサンプルデータベース Northwind の Products テーブルを使って、上に書いた案でできることを確認したのでその内容を書いておきます。

Northwind の Products テーブル

SupplierID が質問者さんの [識別子] と思ってください。int 型で NULL 可になってますが、それは本質的なところとは関係ないはずです。

イメージ説明

Model (エンティティクラス)

イメージ説明

View

イメージ説明

Controller / Action Method

イメージ説明

結果 (ブラウザの表示)

イメージ説明

投稿2020/12/02 06:18

編集2020/12/02 08:48
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

K-actus

2020/12/02 07:22

ありがとうございます。 ひとまず頂いたコードを元に作成してみようと思います。
退会済みユーザー

退会済みユーザー

2020/12/02 08:38

質問者さんの DB では検証しようがありませんが、似たような内容の Microsoft のサンプルデータベースで検証したので、後でその内容を回答欄に追記しておきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問