关于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

只要没有红色的,恭喜你,你的数据库已经更新。剩下的就……听天由命吧…… 要么一片祥和,要么腥风血雨……

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值