質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.50%
MVC

MVC(Model View Controller)は、オブジェクト指向プログラミングにおけるモデル・ビュー・コントローラーの総称であり、ソフトフェア開発で使われている構築パターンとしても呼ばれます。

ASP.NET

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

ASP.NET MVC Framework

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

Q&A

解決済

1回答

2458閲覧

ビューから受け取るデータがクラスのメンバの一部である場合の差分更新方法

pg_c_sharp

総合スコア1

MVC

MVC(Model View Controller)は、オブジェクト指向プログラミングにおけるモデル・ビュー・コントローラーの総称であり、ソフトフェア開発で使われている構築パターンとしても呼ばれます。

ASP.NET

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

ASP.NET MVC Framework

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

0グッド

0クリップ

投稿2021/06/12 10:16

編集2021/06/12 12:28

前提・実現したいこと

ASP.NET MVC5で、DB(SQL Server2016)から取得したデータをビューに表示、ユーザーがビューで入力したデータを受け取ってDBに反映するという処理を実装しています。

実際よりt_item_listのメンバ等を簡略化していますが、以下のようなコードです。

該当のソースコード

C#

1public class t_item_list 2{ 3 public long id { get; set; } 4 public string item_name { get; set; } 5 public string information { get; set; } 6 public System.DateTime create_date { get; set; } 7} 8

C#

1public ActionResult Edit(string id) 2{ 3 t_item_list t_item_list = db.t_item_list.Find(id); 4 return View(t_item_list); 5} 6

C#

1[HttpPost] 2public ActionResult Edit([Bind(Include = "id,item_name,information")] t_item_list t_item_list) 3{ 4 if (ModelState.IsValid) 5 { 6 db.Entry(t_item_list).State = EntityState.Modified; 7 db.SaveChanges(); 8 return RedirectToAction("Index"); 9 } 10 return View(t_lend_items); 11}

(また、クラス「t_item_list」のに対応しているDB側のテーブル[dbo].[t_item_list]は、テーブル生成SQLで記述すると以下のような構造です。)

SQL

1CREATE TABLE [dbo].[t_item_list] ( 2 [id] bigint NOT NULL, 3 [item_name] NVARCHAR (30) NOT NULL, 4 [information] NVARCHAR (50) NULL, 5 [create_date] DATETIME NOT NULL, 6 PRIMARY KEY CLUSTERED ([id] ASC) 7);

###問題点
この時、create_dateはレコードの生成日時を記録するDBの内部処理用のレコード(※1)なので、Edit(POSTされた結果を受け取って更新する側)では受け取っていません。
このコードをそのまま実行すると、SaveChanges()で、create_dateがNULLだというエラーになります。
(※もし仮に実行できたとしても、crerate_dateがnullになってしまうのでやはり問題ですが)

(※1)
「create_date」は当該レコードが生成された日時を記録するフィールドです。
レコードを新規追加する際にその日時が記録され、その後は変更されません。

試したこと

以下のように、DBから更新したいレコードの値を呼び出しておき、そこにビューからPOSTされたデータを代入するというコードに変更することで、エラーは出なくなりました。

C#

1[HttpPost] 2public ActionResult Edit([Bind(Include = "id,item_name,information")] t_item_list t_item_list_temp) 3{ 4 t_lend_items t_lend_items = db.t_lend_items.Find(t_item_list_temp.id); 5 t_lend_items.item_name = t_item_list_temp.item_name; 6 t_lend_items.information = t_item_list_temp.information; 7 8 if (ModelState.IsValid) 9 { 10 db.SaveChanges(); 11 return RedirectToAction("Index"); 12 } 13 return View(t_lend_items); 14}

知りたいこと

今回行った対処が一般的な手法なのか、推奨されない手法なのかを知りたいです。より具体的には以下2点を知りたいです。

  1. ビューからPOSTで受け取るデータがレコードの一部である場合、DBからレコードを取得した上でビューから受け取った分を代入するという方法は適切ですか?違うとしたら、より良い方法はありますか?

  2. 今回の様にレコードの一部のみを受け取る場合、POSTを受け取る側のEditメソッドでは、「t_item_list」にバインドするのではなく、必要分のみをメンバに持つ(今回ならid,item_name,informationのみを持つ)クラスを定義し、そのクラスにバインドさせる形で受け取る方が適切ですか?

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2021/06/12 11:54

DB は何ですか? SQL Server? 「create_dateはレコードの生成日時を記録するDBの内部処理用のレコード」というのは具体的に何ですか?
pg_c_sharp

2021/06/12 12:14 編集

確認ありがとうございます DBはSQL Server(2016)です。 「create_date」は当該レコードが生成された日時を記録する フィールドです。 レコードを新規追加する際にその日時が記録され、その後は 変更されません。 (クラス「t_item_list」の元になっているテーブル[dbo].[t_item_list] は、テーブル生成SQLで記述すると以下のような構造です。) CREATE TABLE [dbo].[t_item_list] ( [id] bigint NOT NULL, [item_name] NVARCHAR (30) NOT NULL, [information] NVARCHAR (50) NULL, [create_date] DATETIME NOT NULL, PRIMARY KEY CLUSTERED ([id] ASC) );
退会済みユーザー

退会済みユーザー

2021/06/12 12:19

上記は追加情報として質問欄を編集して追記願います。ここは「質問への追記・修正の依頼」をする場所ですので。また、初期画面では閉じているので、読まない人もいますから。
pg_c_sharp

2021/06/12 12:29

指摘ありがとうございます。この欄の用途を誤解していました。 質問欄自体に追記しました。
退会済みユーザー

退会済みユーザー

2021/06/13 06:14

質問者さん、無言ですが、回答したのでそれに対するフィードバックを返してください。役に立った / 立たなかったぐらいならすぐ返せるのでは。役に立たなかったならどこがダメだったかを書けばより期待するものに近い回答が出てくるかも。
pg_c_sharp

2021/06/13 11:37

すいません。一番知りたかった部分について回答をしていただのですが、現在返信を推敲していますので今しばらくお待ちください。ありがとうございました。
guest

回答1

0

ベストアンサー

質問にある「試したこと」の方法で良いと思います。

もしくは、ユーザーの編集対象ではない主キーの id は View では @Html.HiddenFor(model => model.id) となっていると思いますが、create_date も同様に @Html.HiddenFor(model => model.create_date) とするとか。

データベースを更新 (UPDATE) するには、SaveChanges メソッドをコンテキストに適用する前に、当該エンティティの State を EntityState.Modified に設定するのですが、その方法には、質問にある「該当のソースコード」の、

db.Entry(t_item_list).State = EntityState.Modified;

のように、編集したオブジェクトをコンテキストにアタッチしそのエンティティの State を Modified に設定する方法と、「試したこと」のコードのようにする方法がありますが、どちらでも OK です。

ちなみに、「試したこと」のコードのように、コンテキストから更新するエンティティを取得し、そのプロパティを使って内容を書き換えると、前の値から変更された場合は自動的にそのエンティティに Modified マークが付けられます。(ちなみに、前の値と同じ値を設定した場合は Unchanged のままとなります)

他に、以下のチュートリアルにある TryUpdateModel メソッドを使うという方法もありそうです。

チュートリアル: ASP.NET MVC アプリで EF を使用して関連データを更新する
https://docs.microsoft.com/ja-jp/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/updating-related-data-with-the-entity-framework-in-an-asp-net-mvc-application

投稿2021/06/12 12:57

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

pg_c_sharp

2021/06/13 12:12

回答ありがとうございました。 アプローチ方法としては、DBからコントローラでデータを取得してビューに渡す時点でレコードに含まれるデータを一式ビューに渡し、POST時もビューから一式受け取ってそのままDBに返す方法と、ビューとやり取りする部分は一部にしておいて更新時に再度DBからデータを取得し、そこ(=コンテキスト)に差分を書き込む方法の二通りがあり、今回私が試したのは後者だったという訳ですね。 前者ならDBに対して更新時に再度問い合わせる必要はないが代わりに比較すべき差分がないので更新はレコード全体になる、後者の場合はDBからの再取得は必要となるもののDBから取得したデータを元に代入時に差分比較が行われ、代入したもののうち実際に変更されたカラムのみが更新処理の対象となるということですね。 (この回答を受けて実際にVisual Studioで更新時のSQLを読んでみたのですが、確かに後者ではUPDATE文の中で更新されたプロパティのみが含まれていることが確認出来ました) 後はこれらの特性の違いを把握して、処理内容によって都合がいい方をどちらを選択すべきということなのですね。 ちなみに、質問文には書いていなかったのですが、今回の質問の大元になった疑問として、「実はコンテキストが一度読みだしたデータを保持していて、ビュー表示用に取得したデータがそのまま残っているので、更新を掛ける時点でもそこと比較すればよい」等ということがあったりするんじゃないのかと思っていたのですが、↓等をみるとビュー表示用に読みだしたデータはPOSTが戻ってくるときにはもう破棄されているということなのですね。 https://teratail.com/questions/308162 色々と疑問だったことが理解でき、また現在試しているアプローチの方針は 合っているということがわかりました。 ありがとうございました。 ※TryUpdateModelに関しては、名前は知っているという程度の理解度でした。 教えていただいたリンクも一読はしてみたのですが、一読ではまだ理解が追い付かなかったので、引き続き熟読してみようと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問