• ASP.NET Core 3.1系列(16)——Entity Framework Core之Code First


    1、前言

    前一篇博客介绍了EFCore中的DB First开发模式,该模式可以根据数据库生成实体类和数据库上下文,因此适用于数据库已经存在的场景。而与之相对应的,Code First主要是根据自定义的实体类和数据库上下文反向构建数据库,因此也可以看做是DB First的逆过程,下面开始介绍。

    2、定义实体类和数据库上下文

    新建一个Web API项目,使用NuGet引入如下组件:

    Microsoft.EntityFrameworkCore
    Microsoft.EntityFrameworkCore.SqlServer
    Microsoft.EntityFrameworkCore.Design
    Microsoft.EntityFrameworkCore.Tools
    
    • 1
    • 2
    • 3
    • 4

    新建实体类Author、数据库上下文DaoDbContext,如下图所示:

    在这里插入图片描述
    DaoDbContext的代码如下所示:

    using App.Models;
    using Microsoft.EntityFrameworkCore;
    
    namespace App.Context
    {
        public class DaoDbContext : DbContext
        {
            public DaoDbContext()
            {
    
            }
    
            public DaoDbContext(DbContextOptions<DaoDbContext> options) : base(options)
            {
    
            }
    
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder.UseSqlServer("Data Source=DSF-PC;Initial Catalog=Dao;User ID=sa;Password=123456;");
            }
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Author>();
                base.OnModelCreating(modelBuilder);
            }
    
            public virtual DbSet<Author> Author { get; set; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    Author先不用添加代码:

    namespace App.Models
    {
        public class Author
        {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3、基于Data Annotations的Code First

    3.1、定义主键——[Key]

    Code First模式通过实体类生成数据表,而数据表必然含有一个主键,在Code First中可以使用[Key]来标识实体类中的主键字段,代码如下:

    using System.ComponentModel.DataAnnotations;
    
    namespace App.Models
    {
        public class Author
        {
            /// 
            /// 主键
            /// 
            [Key]
            public int Id { get; set; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    NuGet控制台中输入如下命令:

    Add-Migration m1
    
    • 1

    然后更新数据库:

    Update-Database
    
    • 1

    打开数据库查看新生成的Author数据表,可以看到主键字段Id已经生成,如下图所示:

    在这里插入图片描述

    3.2、定义文本长度——[StringLength]、[MinLength]、[MaxLength]

    在数据库中,部分字段的类型可能是varcharnvarchar,此时可以通过[StringLength][MinLength][MaxLength]对其长度进行标识。在Author中新增NameAddress字段,代码如下:

    using System.ComponentModel.DataAnnotations;
    
    namespace App.Models
    {
        public class Author
        {
            /// 
            /// 主键
            /// 
            [Key]
            public int Id { get; set; }
    
            /// 
            /// 姓名
            /// 
            [MinLength(2)]
            [MaxLength(20)]
            public string Name { get; set; }
    
            /// 
            /// 地址
            /// 
            [StringLength(40)]
            public string Address { get; set; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    NuGet控制台中输入如下命令:

    Add-Migration m2
    
    • 1

    然后更新数据库:

    Update-Database
    
    • 1

    打开数据库查看新生成的Author数据表,可以看到NameAddress字段已经生成,其中Name字段最大长度为20Address字段最大长度为40,如下图所示:

    在这里插入图片描述

    3.3、字段不能为NULL——[Required]

    在数据表中,主键不能为NULL。如果希望其他字段也不能为NULL,则可以使用[Required]进行标识,下面对Author进行修改,规定Name字段不能为空,代码如下:

    using System.ComponentModel.DataAnnotations;
    
    namespace App.Models
    {
        public class Author
        {
            /// 
            /// 主键
            /// 
            [Key]
            public int Id { get; set; }
    
            /// 
            /// 姓名
            /// 
            [Required]
            [MinLength(2)]
            [MaxLength(20)]
            public string Name { get; set; }
    
            /// 
            /// 地址
            /// 
            [MinLength(5)]
            [MaxLength(40)]
            public string Address { get; set; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    NuGet控制台中输入如下命令:

    Add-Migration m3
    
    • 1

    然后更新数据库:

    Update-Database
    
    • 1

    打开数据库查看新生成的Author数据表,可以看到Name被定义为不能为NULL,如下图所示:

    在这里插入图片描述

    3.4、忽略映射字段——[NotMapped]

    在某些情况下,实体类中的部分字段并不需要在数据库中生成对应的字段,此时就可以使用[NotMapped]进行标识。下面对Author进行修改,添加一个Info字段,代码如下:

    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace App.Models
    {
        public class Author
        {
            /// 
            /// 主键
            /// 
            [Key]
            public int Id { get; set; }
    
            /// 
            /// 姓名
            /// 
            [Required]
            [MinLength(2)]
            [MaxLength(20)]
            public string Name { get; set; }
    
            /// 
            /// 地址
            /// 
            [MinLength(5)]
            [MaxLength(40)]
            public string Address { get; set; }
    
            /// 
            /// 信息
            /// 
            [NotMapped]
            public string Info { get => $"姓名:{Name},地址:{Address}"; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    NuGet控制台中输入如下命令:

    Add-Migration m4
    
    • 1

    然后更新数据库:

    Update-Database
    
    • 1

    打开数据库查看新生成的Author数据表,可以看到实体类中的Info字段并没有创建对应的字段,如下图所示:

    在这里插入图片描述

    3.5、定义列名——[Column]

    在某些情况下,我们希望手动设置某个字段在数据表中对应的列名,此时就可以使用[Column]进行标识,下面对Author进行修改,给每个字段加上前缀Author_,代码如下:

    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace App.Models
    {
        public class Author
        {
            /// 
            /// 主键
            /// 
            [Key]
            public int Id { get; set; }
    
            /// 
            /// 姓名
            /// 
            [Required]
            [MinLength(2)]
            [MaxLength(20)]
            [Column("Author_Name")]
            public string Name { get; set; }
    
            /// 
            /// 地址
            /// 
            [MinLength(5)]
            [MaxLength(40)]
            [Column("Author_Address")]
            public string Address { get; set; }
    
            /// 
            /// 信息
            /// 
            [NotMapped]
            public string Info { get => $"姓名:{Name},地址:{Address}"; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    NuGet控制台中输入如下命令:

    Add-Migration m5
    
    • 1

    然后更新数据库:

    Update-Database
    
    • 1

    打开数据库查看新生成的Author数据表,可以看到数据表中的字段已经加上了Author_前缀,如下图所示:

    在这里插入图片描述

    3.6、定义表名——[Table]

    如果希望手动设置数据表名称,则可以使用[Table]进行标识,下面对Author进行修改,将表名设置为T_AuthorInfo,代码如下:

    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace App.Models
    {
        [Table("T_AuthorInfo")]
        public class Author
        {
            /// 
            /// 主键
            /// 
            [Key]
            public int Id { get; set; }
    
            /// 
            /// 姓名
            /// 
            [Required]
            [MinLength(2)]
            [MaxLength(20)]
            [Column("Author_Name")]
            public string Name { get; set; }
    
            /// 
            /// 地址
            /// 
            [MinLength(5)]
            [MaxLength(40)]
            [Column("Author_Address")]
            public string Address { get; set; }
    
            /// 
            /// 信息
            /// 
            [NotMapped]
            public string Info { get => $"姓名:{Name},地址:{Address}"; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    NuGet控制台中输入如下命令:

    Add-Migration m6
    
    • 1

    然后更新数据库:

    Update-Database
    
    • 1

    打开数据库查看新生成的Author数据表,可以看到数据表的名称已经修改为T_AuthorInfo,如下图所示:

    在这里插入图片描述

    3.7、定义外键——[ForeignKey]

    如果存在一对多的情况,则可以使用[ForeignKey]对外键进行标识。新建一个实体类Book,该类包含一个外键AuthorId,代码如下:

    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace App.Models
    {
        public class Book
        {
            /// 
            /// 主键
            /// 
            [Key]
            public int Id { get; set; }
    
            /// 
            /// 书名
            /// 
            [StringLength(30)]
            public string BookName { get; set; }
    
            /// 
            /// 外键
            /// 
            public int? AuthorId { get; set; }
    
            /// 
            /// 导航属性
            /// 
            [ForeignKey("AuthorId")]
            public virtual Author Author { get; set; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    由于AuthorBook是一对多的关系,因此Author中需要定义一个Book集合,代码如下:

    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace App.Models
    {
        public class Author
        {
            public Author()
            {
                Book = new HashSet<Book>();
            }
    
            /// 
            /// 主键
            /// 
            [Key]
            public int Id { get; set; }
    
            /// 
            /// 姓名
            /// 
            [Required]
            [MinLength(2)]
            [MaxLength(20)]
            public string Name { get; set; }
    
            /// 
            /// 地址
            /// 
            [MinLength(5)]
            [MaxLength(40)]
            public string Address { get; set; }
    
            /// 
            /// 信息
            /// 
            [NotMapped]
            public string Info { get => $"姓名:{Name},地址:{Address}"; }
    
            /// 
            /// 导航属性
            /// 
            public virtual ICollection<Book> Book { get; set; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    最后更新一下DaoDbContext,代码如下:

    using App.Models;
    using Microsoft.EntityFrameworkCore;
    
    namespace App.Context
    {
        public class DaoDbContext : DbContext
        {
            public DaoDbContext()
            {
    
            }
    
            public DaoDbContext(DbContextOptions<DaoDbContext> options) : base(options)
            {
    
            }
    
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder.UseSqlServer("Data Source=10.8.59.253;Initial Catalog=Dao;uid=sa;pwd=gis1a6b7c!Z;");
            }
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Author>();
                modelBuilder.Entity<Book>();
                base.OnModelCreating(modelBuilder);
            }
    
            public virtual DbSet<Author> Author { get; set; }
            public virtual DbSet<Book> Book { get; set; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    NuGet控制台中输入如下命令:

    Add-Migration m7
    
    • 1

    然后更新数据库:

    Update-Database
    
    • 1

    打开数据库,可以看到数据表Book已经生成,同时外键AuthorId也已经成功创建,如下图所示:

    在这里插入图片描述
    到此为止,我们已经熟悉了基本的Code First操作。其实Data Annotations中还包含很多其他的属性标识,如[Range][Comment][RegularExpression]等,有兴趣的同志可自行深入研究。

    4、基于Fluent API的Code First

    上面介绍了基于Data AnnotationsCode First,该模式主要是通过给实体类打标签定义数据表。在EF Core中,还有一种使用Fluent API定义数据表的方法,下面开始介绍其使用方法,项目结构如下图所示:

    在这里插入图片描述

    定义AuthorBook,代码如下:

    using System.Collections.Generic;
    
    namespace App.Model
    {
        public class Author
        {
            public Author()
            {
                Book = new HashSet<Book>();
            }
    
            /// 
            /// 主键
            /// 
            public int Id { get; set; }
    
            /// 
            /// 姓名
            /// 
            public string Name { get; set; }
    
            /// 
            /// 地址
            /// 
            public string Address { get; set; }
    
            /// 
            /// 导航属性
            /// 
            public virtual ICollection<Book> Book { get; set; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    namespace App.Model
    {
        public class Book
        {
            /// 
            /// 主键
            /// 
            public int Id { get; set; }
    
            /// 
            /// 书名
            /// 
            public string BookName { get; set; }
    
            /// 
            /// 外键
            /// 
            public int? AuthorId { get; set; }
    
            /// 
            /// 导航属性
            /// 
            public virtual Author Author { get; set; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    AuthorConfigurationBookConfiguration需要继承IEntityTypeConfiguration<>接口,代码如下:

    using App.Model;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore.Metadata.Builders;
    
    namespace App.Config
    {
        public class AuthorConfiguration : IEntityTypeConfiguration<Author>
        {
            public void Configure(EntityTypeBuilder<Author> builder)
            {
                // 定义主键
                builder.ToTable("Author").HasKey(p => p.Id);
                // 定义字段Name
                builder.Property(p => p.Name).IsRequired().HasMaxLength(20);
                // 定义字段Address
                builder.Property(p => p.Address).HasMaxLength(40);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    using App.Model;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore.Metadata.Builders;
    
    namespace App.Config
    {
        public class BookConfiguration : IEntityTypeConfiguration<Book>
        {
            public void Configure(EntityTypeBuilder<Book> builder)
            {
                // 定义主键
                builder.ToTable("Book").HasKey(p => p.Id);
                // 定义字段BookName
                builder.Property(p => p.BookName).IsRequired().HasMaxLength(20);
                // 定义外键AuthorId
                builder.HasOne(p => p.Author)
                       .WithMany(p => p.Book)
                       .HasForeignKey(p => p.AuthorId)
                       .OnDelete(DeleteBehavior.Cascade)
                       .HasConstraintName("FK_Book_Author");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    最后添加DaoDbContext部分,代码如下:

    using App.Config;
    using App.Model;
    using Microsoft.EntityFrameworkCore;
    
    namespace App.Context
    {
        public class DaoDbContext : DbContext
        {
            public DaoDbContext()
            {
    
            }
    
            public DaoDbContext(DbContextOptions<DaoDbContext> options) : base(options)
            {
    
            }
    
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder.UseSqlServer("Data Source=DSF-PC;Initial Catalog=Dao;User ID=sa;Password=123456;");
            }
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                modelBuilder.ApplyConfiguration(new AuthorConfiguration());
                modelBuilder.ApplyConfiguration(new BookConfiguration());
                base.OnModelCreating(modelBuilder);
            }
    
            public virtual DbSet<Author> Author { get; set; }
            public virtual DbSet<Book> Book { get; set; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    NuGet控制台中输入如下命令:

    Add-Migration mig
    
    • 1

    然后更新数据库:

    Update-Database
    
    • 1

    打开数据库,可以看到数据表AuthorBook已经生成,同时外键AuthorId也已经成功创建,如下图所示:

    在这里插入图片描述

    5、结语

    本文主要介绍了EF Core中的Code First模式,在实际开发过程中更推荐使用Fluent API的方式,因为该方法耦合性较低且更加灵活。

  • 相关阅读:
    使用mysql语句进行分组查询
    美国电力传输公司使用 OpenText 内容管理平台建立具有成本效益的记录管理流程
    excel系列(二) - 利用 easypoi 快速实现 excel 文件导入导出
    如何连接到sqlplus
    MySQL 保姆级教程(六):用通配符进行过滤
    CCF秀湖会议:“第五存储架构”引发关注
    开发者 | 第四期MindSpore两日集训营记录
    Vue.js2+Cesium1.103.0 十二、绑定多个 DOM 弹窗,并跟随视角实时更新位置
    壳聚糖基原位水凝胶包载角膜缘干细胞/纳米壳聚糖骨形态发生蛋白水凝胶的研究制备
    【读书笔记】《中央帝国的财政密码》
  • 原文地址:https://blog.csdn.net/HerryDong/article/details/128115133