iT邦幫忙

2021 iThome 鐵人賽

DAY 16
0
自我挑戰組

ASP.NET Core & Blazor系列 第 18

Day 18 修改add()方法

假設今天有個狀況是這樣:有一筆日誌,新增第二筆但還沒送出前,想將第一筆刪除,這時會發生什麼事呢?

竟然出錯了!明明只是將要刪除的PostId送到後端去,為什麼會有這樣的錯誤訊息?
https://ithelp.ithome.com.tw/upload/images/20210918/20140893JdJcKcNZWZ.png

這就要說到 C# 的特性了,C# 是物件導向(Object-Oriented Programming, OOP)語言,也就是說任何東西包括資料、方法都能變成物件,BlogPost就是一個個物件,除了物件這種參考型別,也有單純的intbool等實質型別。
(註:string分類上是參考型別,但語法上卻是實質型別,這是為了避免無數的 string 塞爆記憶體。)

實質型別的意思是:兩個實質型別之間的異動不會影響彼此。定義一個變數int a = 0;,再定義int b = a;b等於 0 這沒問題,這時候如果再賦值b = 3;ab就不相等了,彼此間不會影響對方。下圖用 LINQPad 示範,Dump()的意思是將該變數顯示在下方Results區塊,可以看到即便中間改動b的值,a也不受影響。
https://ithelp.ithome.com.tw/upload/images/20210918/20140893KPklBKGUNa.png

參考型別則是:B 物件如果來自 A 物件,不論哪個物件變動,另一個就會跟著變動。可以看到下圖在12行將B物件的Title改為"BB",結果A物件的Title也跟著變了。
https://ithelp.ithome.com.tw/upload/images/20210918/2014089345Lz65nHTZ.png

那這些跟Blog有什麼關係呢?我們看後端BlogRepository.csGetBlog(),可以看到這邊將blog回傳,前端BlogBase.razor.cs這邊接起來後,一旦觸發add()就會在Blog.Posts新增一筆PostModel
https://ithelp.ithome.com.tw/upload/images/20210918/20140893bzq52KO27t.png
https://ithelp.ithome.com.tw/upload/images/20210918/20140893dneDfnL216.png

前端按下Delete按鈕後,後端PostRepository.csDeletePost()這邊會觸發SaveChanges(),這時候的Blog.Posts會有一筆沒有BlogTitleContentPostModel,這筆根本還沒按過Submit按鈕經由後端存到資料庫,是只存在於前端的資料,但是觸發SaveChanges()的時候卻試圖將這筆資料存進資料庫,TitleContent是不能為null的,自然就出錯了。
https://ithelp.ithome.com.tw/upload/images/20210918/20140893CvJ9C9aj61.png
https://ithelp.ithome.com.tw/upload/images/20210918/20140893x7tgJZV3xf.png

另外如果單純將資料庫的Posts撈出來,是看不到那一筆資料的,因為那是跟著BlogPostModel
https://ithelp.ithome.com.tw/upload/images/20210918/20140893RjKJlgLIXk.png

要解決這問題有幾種方法,第一種是將BlogPost完全拆開,兩者各有自己的前端畫面,不過如果現實情況的專案遇到這種坑 (沒錯,這是筆者給自己挖的坑…),往往不會有時間做這種重構。

第二種方法是當後端PostRepository.cs收到沒有TitlePostModel時,回傳提示訊息。
https://ithelp.ithome.com.tw/upload/images/20210918/20140893jPw1FSYYie.png

前端PostBase.razor.cs改以deleted.IsSuccess判斷,刪除成功則將PostId傳給Blog將該筆Post從畫面刪除,失敗的話提示失敗的原因。
https://ithelp.ithome.com.tw/upload/images/20210918/2014089336EjhwuELS.png
https://ithelp.ithome.com.tw/upload/images/20210918/20140893WvMQi4BvH1.pnghttps://ithelp.ithome.com.tw/upload/images/20210918/20140893g4BcSGxV4l.pnghttps://ithelp.ithome.com.tw/upload/images/20210918/20140893plqzuDLxih.png

雖然以工程師的角度來看這樣避免了錯誤,但以 UX (User Experience) 角度來看根本就是莫名其妙,為什麼刪除一筆日誌還要限制不能有空的日誌?所以就要用第三種方法。

第三種是建立 ViewModel,畫面的CRUD都針對 ViewModel 處理,之後才一一 Mapping 回去 Model。

所謂的 ViewModel 是指不存在於資料庫但又希望呈現在畫面上的欄位,例如有張 table Employee裡面有兩個欄位FirstNameLastName,存進資料庫時分開存,但顯示時希望動些手腳 (例如要組合起來且全大寫),可以把兩個欄位都丟到前端後再處理,由使用者的瀏覽器分擔,也可以先在後端處理好再用 ViewModel 承接丟到前端。

另一個例子是信用卡,table CreditCard存有使用者的信用卡號、三位數認證碼、出生年月日,大家應該常常網購,刷卡時會讓使用者看到信用卡末四碼,這種機密隱私資料總不可能 16 碼都丟到前端處理吧?這時就需要在後端處理後再由 ViewModel 傳到前端了。

我們先建立 BlogViewModelPostViewModel,因為是 ViewModel 所以不需要用跟資料庫相關的[Key]attribute,有使用到Model的地方都改成ViewModel
https://ithelp.ithome.com.tw/upload/images/20210918/20140893RBojTbfITT.pnghttps://ithelp.ithome.com.tw/upload/images/20210918/201408935nDTwSmvNN.png

接著修改後端BlogRepository.cs,畫面呈現改成 ViewModel,資料存取沿用 Model,可以看到 28 到 48 行手動做 Mapping。
https://ithelp.ithome.com.tw/upload/images/20210918/20140893kijKXPY7EM.pnghttps://ithelp.ithome.com.tw/upload/images/20210918/20140893W9GNQCFNOR.png

PostRepository.csCreatePost()也是一樣,DeletePost()則把原本的else區塊對Blog.Posts的判斷移除。
https://ithelp.ithome.com.tw/upload/images/20210918/201408931NKUN2tbvV.pnghttps://ithelp.ithome.com.tw/upload/images/20210918/20140893jPCnJ5g1Lt.png

BlogBase.razor.csPostBase.razor.cs把原本用到的 Model 改成 ViewModel。
https://ithelp.ithome.com.tw/upload/images/20210918/20140893QajIbq8UWT.pnghttps://ithelp.ithome.com.tw/upload/images/20210918/20140893L32mxV5cPU.pnghttps://ithelp.ithome.com.tw/upload/images/20210918/20140893Ve4vTDJPIc.png

這時候來建立新資料,不過建立第二筆後緊接著要刪除第二筆,卻發生找不到 Post的問題,這是為什麼?
https://ithelp.ithome.com.tw/upload/images/20210918/20140893KRzRhYl1vo.png

原來第二筆雖然進入資料庫了,但我們沒有重新將資料撈回來,畫面的Blog.Posts第二筆的PostId仍然是0。
https://ithelp.ithome.com.tw/upload/images/20210918/20140893R8gcZlQGOt.png

為了讓Blog.Posts知道要重撈資料庫,我們要在PostBase.razor.cs新增EventCallback,告知BlogBase.razor.cs再執行一次loadData(),因為是告知而已,就不用傳<TValue>
https://ithelp.ithome.com.tw/upload/images/20210918/20140893i9aBOJbfF3.png
https://ithelp.ithome.com.tw/upload/images/20210918/20140893KpPKmWy6Oi.pnghttps://ithelp.ithome.com.tw/upload/images/20210918/2014089302zsZxwxUI.pnghttps://ithelp.ithome.com.tw/upload/images/20210918/20140893Vn2RcWbChg.png

然後在新增第二筆之後立刻刪除,就會正常了。新增第二筆後再新增第三筆,刪除第二筆也會正常。

(註:如果看到下圖的錯誤訊息,有可能是 Visual Studio 的問題,先試試重開Visual Studio。)
https://ithelp.ithome.com.tw/upload/images/20210918/20140893aiEm5L6jZr.png

Ref: .NET Stack and Heap

Ref: In C#, why is String a reference type that behaves like a value type?

Ref: What is ViewModel in MVC?

Ref:Understanding ViewModel in ASP.NET MVC


上一篇
Day 17 建立Blog跟Post
下一篇
Day 19 上傳圖片
系列文
ASP.NET Core & Blazor30

尚未有邦友留言

立即登入留言