(17/30)大家一起學blazor:建立blog和post關聯

(17/30)大家一起學blazor:建立blog和post關聯

接下來就是講跟資料庫關聯的內容了,會著重說明asp.net core 跟ef core,如果有需要改頁面才會說到blazor,這邊會花比較多篇幅著墨。

最后更新 2021/12/19 下午6:56
StrayaWorker
预计阅读 6 分钟
分类
Blazor
专题
一起學blazor系列
标签
.NET C# ASP.NET Core Blazor EF Core

接下來就是講跟資料庫關聯的內容了,會著重說明 asp.net core 跟 ef core,如果有需要改頁面才會說到 blazor,這邊會花比較多篇幅著墨。

Blog

首先要把 Blog 跟 Post 分开,我们先在NavMenu.razor加入指向 Blog 的连接,接着修改Blog.razor,因为Blog.Id必须有值且大于0才是一条博客文件,所以我们在Blog.Id等于0时显示一个表单让用户输入博客名称,另外在BlogModelName加上Requiredattribute,毕竟一个博客要有名字。

不等于 0 的话则显示博客名称及底下的日志,且博客名称改用<h3>元素。

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

有人问那如果Blog.Id小于0呢?因为这个字段是数据库由1开始递增产生,通常不会有这问题,除非有人窜改数据库,真的担心的话可以在数据库加入不可小于 0 的机制。

页面有了,接着来读取文件,我们在根目录建立一个文件夹Repository,建立一个接口IBlogRepository``,Repository 里面再建立一个文件夹Implement,建立一个类 BlogRepository,文件夹结构因人而异,笔者是因为看到同事这样用,觉得可以快速找到接口跟实现很方便。

接口(IBlogRepository)做的事情很简单,就是规范方法;实现(BlogRepository)的 8 到 13 行是依赖注入AppDbContext;29 到 33 行是取得第一条 Blog,31 行的Include() 就是昨天说的不用自己join table的方法,全名为Eager loading,只要建表的时候有建好关联,就可以节省时间,其他还有Explicit loadingLazy loading,都是Entity Framework Core 提供的方便作法,如果是一对一的关联在 SQL 语句会被翻译成Inner Join,一对多则是Left Join

using BlazorServer.Models;

namespace BlazorServer.Repository;

public interface IBlogRepository
{
	Task<ResultViewModel> CreateBlog(BlogModel blog);

	Task<BlogModel> GetBlog();
}

正常来说 Blog 会跟登录用户(User) 绑定,可能有张 Table 记录BlogIdUserId,但目前还没实现登录机制,所以就取第一条数据,如果没有第一条表示还没建立,回传空的 Blog;15 到 27 行则是建立 Blog 的方法,17 行先检查 Blog 是否存在,不存在则建立 Blog,而不论是否存在,都回传类型为ResultViewModel的实例,里面只有两个属性:IsSuccessMessage,前端收到后是否要根据IsSuccess做事,就是前端的事了。

namespace BlazorServer.Models;

public class ResultViewModel
{
	public bool IsSuccess { get; set; }

	public string? Message { get; set; }
}

接着去Program.cs注册刚才写的Repository,有些人可能会问,上面的GuidService跟之前建立的PostService为什么叫Service 这边却叫Repository,这是笔者的习惯,笔者此前开发的 Blazor 项目是多层架构,分别为后端API中间Model前端Blazor,后端处理数据的文件都以Repository作为后缀词,前端取得数据的文件则以Service 作为后缀词,各端怎么分看不同公司的开发模式。后来笔者将Service 抽成另一个工程,再后来又将DbContext 抽成一个工程,都是为了方便日后有其他项目要参考Model 或是只是要调动Service 的话,不需要参考整个Blazor 或是API 项目。

最后就是BlogBase.razor.cs了,代码块A注入 IBlogRepository、IJSRuntime,代码块B所在方法即OnInitializedAsync()改成异步方法,因为取数据、存数据都用异步,所以这边也要跟进;代码块C就是调用刚才写的GetBlog()方法取得 Blog;代码块D是这篇一开始说到的CreateBlog()方法,这里如果成功的话就取得 Blog 数据,失败则用前面说过的JsInteropClasses显示后端来的Message

接著讓我們輸入博客名稱,輸入提交後,可以看到頁面不同了,去看資料庫,blog 也能看到一條數據。

Post

前面说完 Blog,今天来说 Post 的部分,先建立IPostRepositoryPostRepository,里面只有CreatePost()DeletePost()两个方法,那怎么没有取得单篇的GetPost()或是多篇的GetPosts()方法呢?因为 Blog 的GetBlog()已经带入 Posts 所以不需要GetPosts(),如果之后有需要看到单个日志的话,再建立GetPost()

(註:筆者參與的系統不多,也不知道這樣的系統設計是否常見,只是因為一開始沒有完整設計才會這樣做。)

CreatePost()如果在数据库找不到 Post 就新增一条,找得到就修改,既然要修改就要有修改时间,所以我们在PostModel.cs加上UpdateDateTime字段,用命令Add-Migration新增一个 Migration 后再使用命令Update-Database更新数据库,然后去Program.cs注册这个新建的 Repository。

(註:前面說過的內容筆者就不附圖了,避免占版面。)

再来去PostBase.razor.cs注入PostRepository,修改一下DeletePost(),新增一个按下Submit按钮会触发的事件CreatePost()PostBase.razor加上前面加入的更新时间UpdateDateTimeOnValidSubmit="CreatePost"Submit按钮的type改成submit,让表单真的有提交功能。

接下来就是重头戏了,我们回到BlogBase.razor.cs,把postId删除,因为用不到了,Add()则改成传BlogIdCreateDateTime到新的 PostModel,让这条日志知道是跟着哪个 Blog。

产生CreateDateTimeUpdateDateTime有两种选择:在前端或后端决定,至于哪端看系统如何设计,因为这边在 add 一条 Post 的时候要避免看到0001-01-01 12:00:00这样的尴尬时间,所以我们在前端给DateTime.Now,至于后端要不要覆盖掉前端来的时间,就是设计问题了。

輸入內容後點擊 submit 按鈕,我們的第一篇日誌就創建成功了!

(註:因為這邊的操作是以前學的,也忘記在哪裡看的,就附上學習來源)

  1. Blazor tutorial for beginners
  2. Loading Related Data

註:本文代碼通過 .net 6 + visual studio 2022 重構,可點擊原文連結與重構後代碼比較學習,謝謝閱讀,支持原作者

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2021/12/25

(29/30)大家一起學blazor:blazor單元測試

開發一個系統最無聊的過程大概就是解決bug了,尤其是那種嘗試對null 對象取值的錯誤(`object reference not set to an instance of an object.`),這應該是大部分人剛踏入編程領域最常碰到的問題,為了從枯燥的解決bug過程解脫,這篇就來居間`單元測試`。

继续阅读
同分类 / 同标签 2021/12/25

(28/30)大家一起學blazor:policy-based authorization

之前有說到`asp.net core identity` 使用的是基於`claim` 的驗證,其實`asp.net core identity` 有不同類型的授權方式,最簡單的`登錄授權`、`角色授權`、`claim 授權`,但上述幾種都是以一種方式實現:原則授權(`policy-based authorization`)。

继续阅读