关于EF Core,CodeFirst的总结
废话不说,直接开始:
Nuget引用Microsoft.EntityFrameWorkCore。
实现自己的表(并不限于基础类,可以有自己的构造方法,方法,访问器等,以及神奇的[NotMapped] Attribute来标识这个属性不用翻译成数据库表字段)。
举例说明(相对复杂一点):
public class User
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
/// <summary>
/// 重要属性,暴露给前端用户的uid特指该值,拒绝主键id直接暴露(可以有效防止猜接口获取用户数据)
/// </summary>
public Guid Guid { get; set; }
/// <summary>
/// 名字,不废话
/// </summary>
public string Name { get; set; }
/// <summary>
/// 账户,需做唯一键索引,这里做不了,去DbContext做
/// </summary>
[MaxLength(20)]
[Required]
public string Account { get; set; }
/// <summary>
/// js提交一次hash过的密码,此处存的是二次hash过的
/// </summary>
[Required]
public string PasswordEncrypted { get; set; }
/// <summary>
/// 为枚举类型,翻译到数据库表字段为int,当然如果你定义了enum类型是long,到数据库里也就是bigint了
/// </summary>
public StatusType Status { get; set; }
/// <summary>
/// 校验字段,由程序可判断该条数据是否有效,该字段可简单的在上面的字段全部填完时,调用下面RawChecksum的get访问器获得
/// </summary>
public string Checksum { get; set; }
/// <summary>
/// 获得该条目的校验值,可以用于存入Checksum
/// </summary>
[NotMapped]
public string RawChecksum
{
get
{
return ValidationProvider
.GetValidString(
"userChecksum",
Guid,
Name,
Account,
PasswordEncrypted,
Status
);
}
}
/// <summary>
/// 判断该条记录是否有效
/// </summary>
[NotMapped]
public bool IsValid
{
get
{
return RawChecksum == Checksum;
}
}
}
将表加入到DbContext(用DbSet<T>声明)
通过DbContext的OnModelCreating的override,可以做一些数据库的设置
继续举例:
public class xxxxxDbContext : DbContext
{
//默认初始化
public xxxxxDbContext(DbContextOptions options)
: base(options)
{
}
//表集合
public DbSet<User> Users { get; set; }
//表设置
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasIndex(u => u.Account)
.IsUnique(); //设置唯一键
}
}
然后问题来了,因为这里没有指定连接字串,所以在update到database的时候没法知道要update到哪里。所以微软想出了这么一招:找到入口程序,寻找针对此Context的引用或者配置,得到字串,按照这个连接字串更新库。
具体来说,你可以选择在DbContext的类里实现OnConfiguring的重载,把字串写死到里面(这个方法强烈不推荐,不符合依赖注入的原则,应当把连接字串作为配置项),或者是在入口程序里,实现对此DbContext的带配置参数的调用(DbContext需要像上面的代码一样实现带配置参数的构造方法)。推荐第二种方法,因为这样更符合依赖注入的原则,而且可以使用配置文件,或其他符合配置标准的配置类。
还是举例:
首先是上面贴出来的DbContext,并未重载OnConfiguring方法。
然后是StartUp.cs,ConfigureServices方法:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<xxxxDbContext>(action =>
{
var str = Configuration.GetConnectionString("xxxxConnectionString");
action.UseMySql(str);
});
}
这样便给通过依赖注入给DbContext配置了带配置项的初始化方式。CodeFirst便可以通过这个生成数据库。
接下来要划重点了,踩坑过后总结的结论。需要用CodeFirst生成数据库需要一下条件(假设MVC工程与Entity所在项目分开,如AAA.Mvc和AAA.Model 两个项目,并且Mvc引用了Model):
1. 必须指定一个启动项目,启动项目必须有入口(类库不行)。(这个要特别注意,不要选错了启动项目)
2. 在包管理控制台(其实就是个powershell)内,必须选中默认项目是DbContext所在的项目。
3. DbContext必须由OnConfiguring或者入口程序处配置了连接方式(之前讨论过建议用注入服务的方式)。
4. 整个解决方案必须可以编译通过,没有错误。
满足上述所有条件,才可以执行接下来的语句。在包管理器控制台里输入:
Add-Migration 名称
其中名称可以任意起,但是不能跟之前的重复。
如果没有红色的字出现,恭喜你,你成功的添加了一个差异文件。 如果字体中有黄色的,你就要当心了,因为可能做了删除数据的操作,你需要仔细看看具体的migration文件,到底做了些什么。
接下来看看Migration文件,他们会自动生成并放到Migrations目录下。文件大体上分为两个方法:Up和Down,分别记录了这次操作对应的数据库操作,和如果回滚需要执行的数据库操作。 在Up里如果出现DropTable或者DropColumn,请慎重慎重再慎重。
举例如下:
public partial class n3 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Permission",
table: "Authority");
migrationBuilder.AlterColumn<uint>(
name: "Permission",
table: "GroupUserPermissions",
nullable: false,
oldClrType: typeof(int));
migrationBuilder.AddColumn<uint>(
name: "SelfDeny",
table: "Authority",
nullable: false,
defaultValue: 0u);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "SelfDeny",
table: "Authority");
migrationBuilder.AlterColumn<int>(
name: "Permission",
table: "GroupUserPermissions",
nullable: false,
oldClrType: typeof(uint));
migrationBuilder.AddColumn<int>(
name: "Permission",
table: "Authority",
nullable: false,
defaultValue: 0);
}
}
在生成此文件的同时,会同步更新Migrations目录下的xxxxDbContextModelSnapshot.cs文件,这个文件记录了最后一次Add-Migration后,当前的数据库字段的状态。因此后面的migration其实是根据此快照对比得出的差异内容。所以如果snapshot文件如果跟数据库的实际情况不对应的时候,就有可能出现update到数据库的时候,不能完整的应用数据库更改,甚至出现失败。
需要当心的是,某些update数据库的失败操作,可能导致当前数据库状态与本地记录的状态不匹配,导致之后应用到数据库都失败。此时需要手动修改migration和snapshot文件,确保数据库可以正常应用。这时也要考虑回滚操作的问题,除非你从此不打算回滚了(大部分时间并不需要考虑回滚)
一切准备就绪,这时候就差最后一步了,在包管理器控制台里输入
Updata-Database
只要没有红色的,恭喜你,你的数据库已经更新。剩下的就……听天由命吧…… 要么一片祥和,要么腥风血雨……