從零開始Blazor Server(2)–整合資料庫

  • 2022 年 7 月 28 日
  • 筆記

開篇

上一篇文章我們留了個尾巴,沒有把freesql整合進去,這篇文章我們來整合。

目前的思路呢,是做一個簡單的四不像的RABC,也有用戶、角色、

許可權三部分。

但是其中每個用戶只有一個角色,即用戶和角色之間是一多關係。每個角色可以有多個許可權,即許可權跟角色之間是多多關係。

這樣主要是想說一下freesql怎麼做一多和多多關係。一個正常的RABC用戶和角色之間也應該是多多,並且用戶可能跟許可權也可以有直接的聯繫。但是這個是一樣的,只要許可權列表拿到了,後面就隨便怎麼處理了。

建表

這次我們只建立四個表UserRolePermissionRolePermisson

採用CodeFirst的方式,優先建立Entity,使用Freesql的資料庫同步功能生成表結構。

User表:

[Description("用戶資訊表")]
public class UserEntity : BaseEntity<UserEntity, int>
{
    [Description("用戶名")]
    public string? UserName { get; set; }

    [Description("密碼")]
    public string? Password { get; set; }

    [Description("用戶姓名")]
    public string? Name { get; set; }

    [Description("角色Id")]
    public int RoleId { get; set; }

    [Description("角色")]
    [Navigate(nameof(RoleId))]
    public RoleEntity? Role { get; set; }
}

借著User表解釋一下。

  • 繼承了BaseEntity<UserEntity, int>以後,會添加一個int類型的Id,這個Id是自增的。同時還會自動生成CreateTimeUpdateTimeIsDeletedSort四個欄位,這些都是BaseEntity自帶的功能,使用BaseEntity在查詢的時候會自動攜帶IsDeleted標識,讓我們可以輕鬆軟刪除。

  • Description標籤在Freesql里可以自動生成注釋,由於sqlite不支援注釋,所以這裡沒有用,單純是作為注釋來使用。

  • FreeSql的一多關係,這裡是一的部分,使用起來很簡單,有一個RoleId,然後定義一個RoleEntity,標籤里用Navigate指定通過RoleId來查詢就行了。

Role表:

[Description("角色表")]
public class RoleEntity : BaseEntity<RoleEntity, int>
{
    [Description("角色名稱")]
    public string? Name { get; set; }

    [Description("用戶")]
    [Navigate(nameof(UserEntity.RoleId))]
    public virtual ICollection<UserEntity>? Users { get; set; }

    [Description("許可權")]
    [Navigate(ManyToMany = typeof(RolePermissionEntity))]
    public virtual ICollection<PermissionEntity>? Permissions { get; set; }
}
  • Users是用戶角色一多關係的多的部分,我們只需要指定成ICollectionList也可以。使用Navigate指定關聯的名字為RoleId就可以了。這裡需要注意的是一定是關聯RoleId不是User表的Id,關聯錯了的話這裡的關係就亂掉了。

  • Permissions是角色許可權多多關係的處理部分,屬性寫起來跟一多關係一樣,但是Navigate標籤里不再是綁定一個字元串了,而是用ManyToMany指定一個type,這個type是我們多多關係的中間表。這個表我們下面講。

RolePermission

[Description("角色許可權多多關係表")]
public class RolePermissionEntity : BaseEntity<RolePermissionEntity, Guid>
{
    [Description("角色Id")]
    public int RoleId { get; set; }

    [Description("角色")]
    [Navigate(nameof(RoleId))]
    public RoleEntity? Role { get; set; }

    [Description("許可權Id")]
    public int PermissionId { get; set; }

    [Description("許可權")]
    [Navigate(nameof(PermissionId))]
    public PermissionEntity? Permission { get; set; }
}
  • 嚴格來說,這個表不應該使用BaseEntity的模式,因為它應該是一個聯合主鍵,不應該有個自增主鍵,並且也不需要那些CreateTimeUpdateTimeIsDeletedSort。但是這裡為了省事,就直接用了,只是把主鍵類型改成了Guid,反正我們也不會使用這玩意來排序查詢。也不會影響頻繁刪除的情況下可能出現的溢出問題。

  • 別的沒什麼好說的,就是兩個屬性RoleIdPermissionId就i行了,如果用不著它,那Entity都可以不加。

Permission

[Description("許可權表")]
public class PermissionEntity: BaseEntity<PermissionEntity, int>
{
    [Description("許可權名")]
    public string? Name { get; set; }

    [Description("對應頁面Url")]
    public string? Url { get; set; }
    
    [Description("角色")]
    [Navigate(ManyToMany = typeof(RolePermissionEntity))]
    public virtual ICollection<RoleEntity>? Roles { get; set; }
}

這個表沒什麼好說的了,就是一個多多關係,跟Role的是一樣的。

添加FreeSql

之前我們只是添加了FreeSql的包,沒有掛進程式里,現在我們把它弄到程式里去。

首先我們把連接字元串整到配置文件里去,比如我放在

"Db": {
    "ConnString": "Data Source=|DataDirectory|\\document.db; Pooling=true;Min Pool Size=1"
  },

這樣我們就可以在任何位置 用var conn = Furion.App.Configuration["Db:ConnString"];獲取到連接字元串了。

然後我們需要創建一個freesql的實例

        var freeSql = new FreeSqlBuilder()
            .UseAutoSyncStructure(Furion.App.WebHostEnvironment.IsDevelopment())
            .UseConnectionString(DataType.Sqlite, conn)
            .Build();

這裡的UseAutoSyncStructure是是否開啟自動遷移,如果開啟了,freesql用的時候發現沒有表或者表結構不對,就自動遷移表結構,這個東西生產上不建議用,所以我們就只有在Development的時候使用。

然後我們的Entity都有Entity後綴,這個放到資料庫里不好看,我們就把它給去掉

freeSql.Aop.ConfigEntity += (s, e) =>
        {
            e.ModifyResult.Name = e.EntityType.Name.Replace("Entity", "");
        };

另外如果你需要加前綴,或者改大小寫,改下劃線都可以在這裡金信處理。

因為我們使用的是BaseEntity模式,所以這裡需要初始化

BaseEntity.Initialization(freeSql, null);

最後我們還需要添加一些默認的用戶,角色,許可權

        if (!UserEntity.Where(x => x.UserName == "Admin").Any())
        {
            UserEntity user = new UserEntity()
            {
                UserName = "Admin",
                Password = MD5Encryption.Encrypt("Admin"),
                Name = "張三"
            };
            user.Save();
            

            PermissionEntity homePermission = new PermissionEntity()
            {
                Name = "首頁",
                Url = "/"
            };
            homePermission.Save();
            
            PermissionEntity userPermission = new PermissionEntity()
            {
                Name = "用戶管理",
                Url = "/User"
            };
            userPermission.Save();
            
            RoleEntity role = new RoleEntity()
            {
                Name = "管理員",
                Users = new List<UserEntity>() { user },
                Permissions = new List<PermissionEntity>(){homePermission, userPermission}
            };
            role.Save().SaveMany(nameof(RoleEntity.Users));
            role.SaveMany(nameof(RoleEntity.Permissions));
        }

這裡需要注意,如果有多表的部分需要連其他的表一起更新,那就需要主動調用SaveMany方法,如果不調用,那麼不會自動更新。

源碼在github://github.com/j4587698/BlazorLearn,分支lesson2。