學習ASP.NET Core(02)-分層與EF遷移


上一篇簡單介紹了ASP.NET Core Web項目的結構,本章我們來對項目進行簡單的分層和使用Code First的方式建立數據庫


一、分層

1、項目說明

之前是想做一個Mvc項目,但是主要還是想學習一下前后端分離方面的內容,並且寫博客的目的也是為了學習和記錄一些新的東西,所以就改成ASP.NET Core WebApi項目了;

該系列的目標是建立一個博客項目,功能上目前打算實現用戶管理、文章管理和評論管理三部分的內容,后續再看情況是否進行功能上的擴展

2、分層規划

項目邏輯相對簡單,這里我們采用簡單的三層架構模式,為了解耦為DAL和BLL添加了對應的接口層,另外添加了模型層Model,工具層Common,所以在原先的基礎上添加6個.NET Core類庫項目,項目結構如下圖:

二、建立模型層

1、Model的基類

將model共有的幾個欄位提取出來作為基類。分別是Id、創建時間和是否被刪除,如下:

    /// <summary>
    /// Model的基類
    /// </summary>
    public class BaseEntity
    {
        /// <summary>
        /// 唯一標識Id
        /// </summary>
        public Guid Id { get; set; } = Guid.NewGuid();
        /// <summary>
        /// 創建時間
        /// </summary>
        public DateTime CreateTime { get; set; } = DateTime.Now;
        /// <summary>
        /// 是否被刪除(偽刪除)
        /// </summary>
        public bool IsRemoved { get; set; }
    }

2、用戶類

性別是新建的一個枚舉類,另外后期可能會寫權限方面的東西,所以還有一個用戶等級枚舉類,默認為普通用戶。用戶類要繼承自剛剛新建的基類,並設定一些特性進行限制,比如限定為必填項等等,當然這邊只是對存入數據的一個限定,實際上我們還需要對用戶錄入頁面進行限定,那將是Dto和ViewModel需要處理的事件,這里先不展開

    /// <summary>
    /// 性別枚舉
    /// </summary>
    public enum Gender
    {
        男=0,
        女=1,
        保密=2
    }
    /// <summary>
    /// 用戶等級枚舉
    /// </summary>
    public enum Level
    {
        普通用戶 = 0,
        會員用戶 = 1,
        系統管理員=2
    }
using System;
using System.ComponentModel.DataAnnotations;

namespace BlogSystem.Model
{
    /// <summary>
    /// 用戶
    /// </summary>
    public class User : BaseEntity
    {
        /// <summary>
        /// 賬戶
        /// </summary>
        [Required, StringLength(40)]
        public string Account { get; set; }
        /// <summary>
        /// 密碼
        /// </summary>
        [Required, StringLength(200)]
        public string Password { get; set; }
        /// <summary>
        /// 頭像
        /// </summary>
        public string ProfilePhoto { get; set; }
        /// <summary>
        /// 出生日期
        /// </summary>
        public DateTime BirthOfDate { get; set; }
        /// <summary>
        /// 性別
        /// </summary>
        public Gender Gender { get; set; }
        /// <summary>
        /// 用戶等級
        /// </summary>
        public Level Level { get; set; } = Level.普通用戶;
        /// <summary>
        /// 粉絲數
        /// </summary>
        public int FansNum { get; set; }
        /// <summary>
        /// 關注數
        /// </summary>
        public int FocusNum { get; set; }
    }
}

3、用戶關注

用戶Id和關注用戶Id同樣是來源於用戶表,這里外鍵的處理方法是引入了兩個不同名稱的User,但是這樣處理會存在級聯刪除的問題,但由於我們采用的是偽刪除的方式,並沒有真正的刪除,所以我們會在后面的DbContext方法中關閉級聯刪除以解決此問題

using System;
using System.ComponentModel.DataAnnotations.Schema;

namespace BlogSystem.Model
{
    /// <summary>
    /// 用戶關注表
    /// </summary>
    public class UserFocus : BaseEntity
    {
        /// <summary>
        /// 用戶編號
        /// </summary>
        [ForeignKey(nameof(User))]
        public Guid UserId { get; set; }
        public User User { get; set; }

        /// <summary>
        /// 關注用戶編號
        /// </summary>
        [ForeignKey(nameof(Focus))]
        public Guid FocusId { get; set; }
        public User Focus { get; set; }
    }
}

4、文章類

每篇文章都存在一個發表人,所以我們在這里添加了外鍵,如下:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace BlogSystem.Model
{
    /// <summary>
    /// 文章
    /// </summary>
    public class Article : BaseEntity
    {
        /// <summary>
        /// 文章標題
        /// </summary>
        [Required]
        public string Title { get; set; }
        /// <summary>
        /// 文章內容
        /// </summary>
        [Required, Column(TypeName = "text")]
        public string Content { get; set; }
        /// <summary>
        /// 發表人的Id,用戶表的外鍵
        /// </summary>
        [ForeignKey(nameof(User))]
        public Guid UserId { get; set; }
        public User User { get; set; }
        /// <summary>
        /// 看好人數
        /// </summary>
        public int GoodCount { get; set; }
        /// <summary>
        /// 不看好人數
        /// </summary>
        public int BadCount { get; set; }
        /// <summary>
        /// 文章查看所需等級
        /// </summary>
        public Level Level { get; set; } = Level.普通用戶;
    }
}

5、分類信息

分類信息需要對應到用戶,所以同樣存在一個外鍵對應,建立如下:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace BlogSystem.Model
{
    /// <summary>
    /// 分類
    /// </summary>
    public class Category : BaseEntity
    {
        /// <summary>
        /// 分類名稱
        /// </summary>
        [Required]
        public string CategoryName { get; set; }
        /// <summary>
        /// 分類對應的用戶
        /// </summary>
        [ForeignKey(nameof(User))]
        public Guid UserId { get; set; }
        public User User { get; set; }
    }
}

6、文章對應的分類

每篇文章對應一個或多個分類,同樣存在外鍵對應關系,建立如下:

using System;
using System.ComponentModel.DataAnnotations.Schema;

namespace BlogSystem.Model
{
    /// <summary>
    /// 文章所屬分類
    /// </summary>
    public class ArticleInCategory : BaseEntity
    {
        /// <summary>
        /// 分類Id
        /// </summary>
        [ForeignKey(nameof(Category))]
        public Guid CategoryId { get; set; }
        public Category Category { get; set; }

        /// <summary>
        /// 文章Id
        /// </summary>
        [ForeignKey(nameof(Article))]
        public Guid ArticleId { get; set; }
        public Article Article { get; set; }
    }
}

7、評論功能

評論存在1個帖子對應M個評論,M個評論對應N個回復的情況,所以拆分成文章評論表和評論回復表,如下:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace BlogSystem.Model
{
    /// <summary>
    /// 文章評論表
    /// </summary>
    public class ArticleComment : BaseEntity
    {
        /// <summary>
        /// 評論的文章ID
        /// </summary>
        [ForeignKey(nameof(Article))]
        public Guid ArticleId { get; set; }
        public Article Article { get; set; }
        /// <summary>
        /// 評論用戶ID
        /// </summary>
        [ForeignKey(nameof(User))]
        public Guid UserId { get; set; }
        public User User { get; set; }
        /// <summary>
        /// 評論內容
        /// </summary>
        [Required, StringLength(800)]
        public string Content { get; set; }
    }
}
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace BlogSystem.Model
{
    /// <summary>
    /// 評論回復表
    /// </summary>
    public class CommentReply : BaseEntity
    {
        /// <summary>
        /// 回復指向的評論Id
        /// </summary>
        [ForeignKey(nameof(ArticleComment))]
        public Guid CommentId { get; set; }
        public ArticleComment ArticleComment { get; set; }
        /// <summary>
        /// 回復指向的用戶Id
        /// </summary>
        [ForeignKey(nameof(ToUser))]
        public Guid ToUserId { get; set; }
        public User ToUser { get; set; }
        /// <summary>
        /// 文章ID
        /// </summary>
        [ForeignKey(nameof(Article))]
        public Guid ArticleId { get; set; }
        public Article Article { get; set; }
        /// <summary>
        /// 用戶Id
        /// </summary>
        [ForeignKey(nameof(User))]
        public Guid UserId { get; set; }
        public User User { get; set; }
        /// <summary>
        /// 回復的內容
        /// </summary>
        [Required, StringLength(800)]
        public string Content { get; set; }
    }
}

三、EF遷移

1、數據庫選擇

VS內置了一個"小型"的SQL Server數據庫,這里我們就使用它作為我們的數據庫。右擊依賴項,選擇Nuget包,我們需要安裝Microsoft.EntityFrameworkCore.SqlServer和Microsoft.EntityFrameworkCore,但因為依賴關系安裝Microsoft.EntityFrameworkCore.SqlServer時會一並裝上Microsoft.EntityFrameworkCore,如下:

2、添加DbContext

1、DbContext是實體類與數據庫之間的橋梁,負責與數據庫進行交互。我們添加一個名為BlogSystemContext的類,繼承自DbContext

2、建表時有提到級聯刪除的問題,EF是默認開啟級聯刪除的,即存在外鍵的情況下,刪除一筆數據,關聯數據也會被刪除,上面有說明會存在沖突,所以這里我們重寫方OnModelCreateing法將其關閉。並在OnConfiguring方法中配置數據庫連接,如下:

using Microsoft.EntityFrameworkCore;
using System.Linq;

namespace BlogSystem.Model
{
    public class BlogSystemContext : DbContext
    {
        public BlogSystemContext()
        {
        }

        public BlogSystemContext(DbContextOptions<BlogSystemContext> options) : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //關閉級聯刪除
            var foreignKeys = modelBuilder.Model.GetEntityTypes().SelectMany(m => m.GetForeignKeys()).Where(x => x.DeleteBehavior == DeleteBehavior.Cascade);
            foreach (var foreign in foreignKeys)
            {
                foreign.DeleteBehavior = DeleteBehavior.Restrict;
            }
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            //配置數據庫連接
            optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=BlogSystem;Trusted_Connection=True;");
        }

        public DbSet<Article> Articles { get; set; }

        public DbSet<ArticleComment> ArticleComments { get; set; }

        public DbSet<ArticleInCategory> ArticleInCategories { get; set; }

        public DbSet<Category> Categories { get; set; }

        public DbSet<CommentReply> CommentReplies { get; set; }

        public DbSet<User> Users { get; set; }

        public DbSet<UserFocus> UserFocuses { get; set; }
    }
}

3、使用Migration進行遷移

1、遷移使用Nuget安裝Microsoft.EntityFrameworkCore.Tools和Microsoft.EntityFrameworkCore.Design,由於依賴關系只需要安裝Microsoft.EntityFrameworkCore.Tools即可,安裝后選擇程序包管理控制台,如下:

2、遷移前首先要確認默認項目對應的是BlogSystem.Model輸入添加遷移指令add-migration InitialTable,成功后會出現Migrations文件夾,里面包含數據庫初始化文件和快照文件;執行update-database,成功更新數據表至數據庫

本章完~

本人知識點有限,若文中有錯誤的地方請及時指正,方便大家更好的學習和交流

原創文章聲明


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM