Day 17 建立Blog跟Post

接下来就是跟资料库连结了,会着重说明 ASP.NET Core 跟 EF Core,如果有需要改画面才会说到 Blazor,这边会花比较多篇幅着墨。

Blog

首先要把 Blog 跟 Post 分开,我们先在NavMenu.razor加入指向 Blog 的连结,接着将Blog.razor改写,因为BlogId必须有值且大於0才是一笔部落格资料,所以我们在BlogId等於0时显示一个表单让使用者输入部落格名称,另外在BlogModelBlogName加上Required attribute,毕竟一个部落格要有名字。
https://ithelp.ithome.com.tw/upload/images/20210916/20140893jt7a1ek76B.png
https://ithelp.ithome.com.tw/upload/images/20210916/20140893DdCuf2ULCh.png
https://ithelp.ithome.com.tw/upload/images/20210916/20140893pnE9OPjjb9.png
https://ithelp.ithome.com.tw/upload/images/20210916/20140893R6XmxnQaY3.png

不等於0的话则显示部落格名称及底下的日志,且部落格名称改用<h3>元素。
https://ithelp.ithome.com.tw/upload/images/20210916/20140893BSaJbVYfTv.png

12行有个参数OnValidSubmit="createBlog",代表的是如果表单验证通过,则执行指定的 function createBlog,笔者先写好一个放在那里,等说完取资料的部分会再说明。
(注:如果希望触发OnValidSubmit,必须有个type="submit"的按钮,否则不管怎麽按都没效果)

有人问那如果BlogId小於0呢?因为这个栏位是资料库由1开始递增产生,通常不会有这问题,除非有人窜改资料库,真的担心的话可以在资料库加入不可小於0的机制。

画面有了,接着来取资料,我们在根目录建立一个资料夹 Repository,建立一个介面 interface IBlogRepository,Repository 里面再建立一个资料夹 Implement,建立一个类别 class BlogRepository,资料夹结构因人而异,笔者是因为看到同事这样用,觉得可以快速找到介面跟实作很方便。
https://ithelp.ithome.com.tw/upload/images/20210916/20140893Q7sBFSeWQg.png

介面做的事情很简单,就是规范方法;实作的 13 到 18 行是依赖注入AppDbContext;19 到 27行是取得第一笔 Blog,21行的 Include() 就是昨天说的不用自己 join table的方法,全名为Eager loading,只要建表的时候有建好关联,就可以节省时间,其他还有Explicit loadingLazy loading,都是 Entity Framework Core 提供的方便作法,如果是一对一的关联在 SQL 语法会被翻译成 Inner Join,一对多则是 Left Join
https://ithelp.ithome.com.tw/upload/images/20210916/20140893CffIPEwj5b.pnghttps://ithelp.ithome.com.tw/upload/images/20210916/20140893fdePaXzHVC.png

正常来说 Blog 会跟登入者 (User) 绑定,可能有张 Table 记录BlogIdUserId,但目前还没实作登入机制,所以就取第一笔资料,如果没有第一笔代表还没建立,回传空的 Blog;28 到 43行则是建立 Blog 的方法,30 到 31 行先检查 Blog 是否存在,不存在则建立 Blog,而不论是否存在,都回传型别为ResultViewModel的物件,里面只有两个 Property:IsSuccessMessage,前端收到後是否要根据IsSuccess做事,就是前端的事了。
https://ithelp.ithome.com.tw/upload/images/20210916/20140893vQF5tsNIoo.png

接着去Startup.cs注册刚才写的 Repository,有些人可能会问,上面的GuidService跟之前建立的PostService为什麽叫 Service 这边却叫 Repository,这是笔者的习惯,笔者此前开发的 Blazor 专案是多层式架构,分别为後端 API、中介 Model、前端 Blazor,後端处理资料的档案都以 Repository做为後缀词,前端取得资料的档案则以 Service 做为後缀词,端看不同公司的开发模式。後来笔者将 Service 抽成另一个专案,再後来又将 DbContext 抽成一个专案,都是为了方便日後有其他专案要参考 Model 或是只是要调动 Service 的话,不需要参考整个 Blazor 或是 API 专案。
https://ithelp.ithome.com.tw/upload/images/20210916/20140893JZlmpgNOf0.png

最後就是BlogBase.razor.cs了,16 到 17 行注入IBlogRepositoryIJSRuntime,23 到 27 行改成非同步方法,因为取资料、存资料都用非同步,所以这边也要跟进;28到 31 行就是呼叫刚才写的GetBlog()方法取得 Blog;32 到 43 行是这篇一开始说到的createBlog()方法,这里如果成功的话就取得 Blog 资料,失败则用前面说过的JsInteropClasses显示後端来的Message
https://ithelp.ithome.com.tw/upload/images/20210916/20140893Ipw2YdYxAC.png

接着让我们输入部落格名称,输入送出後,可以看到画面不同了,去看资料库,Blog 也能看到一笔资料。
https://ithelp.ithome.com.tw/upload/images/20210916/20140893JBuGAJrNWK.pnghttps://ithelp.ithome.com.tw/upload/images/20210916/20140893dVW5aFp18S.png

Post

前面说完 Blog,今天来说 Post 的部分,先建立IPostRepositoryPostRepository,里面只有CreatePost()DeletePost()两个方法,那怎麽没有取得单篇的GetPost()或是多篇的GetPosts()方法呢?因为 Blog 的GetBlog()已经带入 Posts 所以不需要GetPosts(),如果之後有需要看到个别日志的话,再建立GetPost()
(注:笔者参与的系统不多,也不知道这样的系统规划是否常见,只是因为一开始没有完整规划才会这样做。)
https://ithelp.ithome.com.tw/upload/images/20210918/20140893aXb75JCo4V.png

CreatePost()如果在资料库找不到 Post 就新增一笔,找得到就修改,既然要修改就要有修改时间,所以我们在PostModel.cs加上UpdateDateTime栏位,用指令Add-Migration新增一个 Migration 後下指令Update-Database更新资料库,然後去Startup.cs注册这个新建的 Repository。
(注:前面说过的内容笔者就不附图了,避免占版面。)
https://ithelp.ithome.com.tw/upload/images/20210918/201408938hr9AiRXxN.png
https://ithelp.ithome.com.tw/upload/images/20210918/20140893TkkfiKJJyk.png

再来去PostBase.razor.cs注入PostRepository,改写一下deletePost(),新增一个按下Submit按钮会触发的事件createPost()PostBase.razor加上前面加入的更新时间UpdateDateTimeOnValidSubmit="createPost"Submit按钮的type改成submit,让表格真的有送出功能。
https://ithelp.ithome.com.tw/upload/images/20210918/201408936CT6sJQqhE.pnghttps://ithelp.ithome.com.tw/upload/images/20210918/20140893qM53nFliAF.png

接下来就是重头戏了,我们回到BlogBase.razor.cs,把postId删除,因为用不到了,add()则改成传BlogIdCreateDateTime到新的PostModel,让这笔日志知道是跟着哪个 Blog。
https://ithelp.ithome.com.tw/upload/images/20210918/201408936wq9VREGo7.png

产生CreateDateTimeUpdateDateTime有两种选择:在前端或後端决定,端看系统如何规划,因为这边在 add 一笔 Post 的当下要避免看到0001-01-01 12:00:00这样的尴尬时间,所以我们在前端给DateTime.Now,至於後端要不要覆盖掉前端来的时间,就是规划问题了。
https://ithelp.ithome.com.tw/upload/images/20210918/20140893sUk3jR9ATg.png

接着把PostModel.Content[MinLength]改成50,因为不想打太多字XD。

打完内容後点击 Submit 按钮,我们的第一篇日志就建立成功了!
https://ithelp.ithome.com.tw/upload/images/20210918/20140893ps5RYGSkbq.png

实际去看资料库,可以看到有资料,跟 BlogId 的关联也正确。
https://ithelp.ithome.com.tw/upload/images/20210918/20140893krfeRRxLEj.png

不过时间好像怪怪的,明明是晚上却显示早上时间,我们把Post.razor@bind:format的小时格式改成大写H,画面的时间就正常了。
https://ithelp.ithome.com.tw/upload/images/20210918/20140893uwaLoBVDat.png

(注:因为这边的操作是以前学的,也忘记在哪里看的,就附上学习来源)
Ref: Blazor tutorial for beginners

Ref: Loading Related Data


<<:  [Python 爬虫这样学,一定是大拇指拉!] DAY03 - 关於 Python (2)

>>:  【Day 03】从零开始的 Line Chatbot-建立专案

Day 7 被动搜查(4)-Email 相关、Harvester、Recon-ng

Email 安全 为什麽要收集各个企业的 Email 信箱,透过了解企业公开在外的 Email,可...

Python 练习

今天要来解APCS的题目,这次是105年10月29的实作题第二题,那我们就开始吧! 题目 解答 a=...

赚外快踩到线

境外诈骗集团使用移动式机房的好处 移动式信号追踪难度高於一般固定位置信号 来电显示为台湾电信发号,降...

【Day16】React Router

一页式网站 SPA SPA 全名 Single Page Applications 只有一个 HTM...

Day27 - 部属到正式环境 (2)

今天的实作内容主要根据教学网站进行。 将应用程序安装到Heroku 环境设定 Heroku主要利用四...