学习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,成功更新数据表至数据库
本章完~
本人知识点有限,若文中有错误的地方请及时指正,方便大家更好的学习和交流