EFCore快速入门(一):实体类的创建、设置和数据库的迁移

122 阅读6分钟

 本文主要用于自己在使用EFCore中的学习过程,方便自己后面回来复习

本文包含了数据表之间的三种关系来做示例:一对多、多对多、一对一

本文使用Visual Studio进行开发,使用Mysql数据库

环境搭建工作:

1、创建一个C#类库项目和一个控制台项目

2、在C#类库项目中安装以下NuGet包:

Microsoft.EntityFrameworkCore

Microsoft.EntityFrameworkCore.Relational

Microsoft.EntityFrameworkCore.Tools

Pomelo.EntityFrameworkCore.MySql(用来连接Mysql数据库,根据数据库不同使用不同的包)

3、在控制台项目安装以下NuGet包:

Microsoft.EntityFrameworkCore.Design(用来进行迁移的包)

4、在控制台项目引用C#类库项目

一、进行模型类的创建和模型类之间的关系分析

这里以作者、书籍、读者、读者拓展信息这几个模型来进行举例

通过分析可知:

作者和书籍是‘一对多’的关系

书籍和读者是‘多对多’的关系

读者和读者拓展信息是‘一对一’的关系

由此建立如下的模型类:

作者类:

internal class Author
{
    public long Id { get; set; }

    // 作者名称
    public string Name { get; set; }

    // 作者性别
    public string? Sex { get; set; }

    // 出版图书
    public List<Book> Books { get; set; }

}

书籍类:

internal class Book
{
    public long Id { get; set; }

    // 书名
    public string? BookName { get; set; }

    // 价格
    public double? Price { get; set; }

    // 对应作者ID(外键)
    public long AuthorId { get; set; }

    // 对应作者
    public Author Author { get; set; }

    // 对应读者
    public List<Reader> Readers { get; set; }

}

读者类:

internal class Reader
{
    public long Id { get; set; }

    // 读者姓名
    public string Name { get; set; }

    // 读者拓展信息ID(外键)
    public long ReaderExternId { get; set; }

    // 读者拓展信息
    public ReaderExtern ReaderExtern { get; set; }

    // 阅读的书籍
    public List<Book> Books { get; set; }
}

读者拓展信息类:

internal class ReaderExtern
{
    public long Id { get; set; }

    // 邮箱
    public string Email { get; set; }

    // 电话
    public string Phone {  get; set; }
}

二、进行模型类的设置

在这里我对模型类的设置采用Fluent API的方法,你也可以采用数据注释的方法,二者的差别可以去查微软的官方文档。另外,我对这些类的设置并不多,所以我写在同一个文件中,如果设置很多的话,可以写在不同的文件中

设置内容如下:

class AuthorConfig : IEntityTypeConfiguration<Author>
{
    public void Configure(EntityTypeBuilder<Author> builder)
    {
        
    }
}


class BookConfig : IEntityTypeConfiguration<Book>
{
    public void Configure(EntityTypeBuilder<Book> builder)
    {
        // 映射表名
        builder.ToTable("Test_Book");
        // 映射属性名
        builder.Property(x=>x.AuthorId).HasColumnName("Test_Author_Id");

        // 作者对书籍:一对多
        builder.HasOne<Author>(b=>b.Author).WithMany(a=>a.Books)
            .HasForeignKey(b=>b.AuthorId);

        // 书籍对读者:多对多
        builder.HasMany<Reader>(b => b.Readers).WithMany(a => a.Books)
            .UsingEntity(j => j.ToTable("Book_Reader_Realation"));
    }
}

class ReaderConfig : IEntityTypeConfiguration<Reader>
{
    public void Configure(EntityTypeBuilder<Reader> builder)
    {
        // 读者对读者拓展信息:一对一
        builder.HasOne<ReaderExtern>(r => r.ReaderExtern).WithOne()
           .HasForeignKey<Reader>(r => r.ReaderExternId);
    }
}

class ReaderExternConfig : IEntityTypeConfiguration<ReaderExtern>
{
    public void Configure(EntityTypeBuilder<ReaderExtern> builder)
    {

    }
}

如上图所示,每个配置类都必须实现IEntityTypeConfiguration接口,并实现其中的Configure方法

作者配置类

我这里没有进行配置,会采用默认配置。

默认配置为:生成的数据表名为模型类名,字段为模型类的属性名。

书籍配置类

我使用了ToTable方法进行了模型类在数据库生成的表名的映射,如果不配置,数据库生成的表名默认为模型类的类名。使用Property方法进行模型类属性的映射。

注意:不要对需要连接查询才能得出的属性使用Property方法,会出现错误。比如书籍类的Author属性和作者类的Books属性,这些属性都是需要连接查询才会取出。

对于模型类之间的关系配置而言,对于一对多的关系,我一般在‘多’端进行配置。

对于作者和书籍‘一对多’的关系:

// 作者对书籍:一对多 
builder.HasOne<Author>(b=>b.Author).WithMany(a=>a.Books)
     .HasForeignKey(b=>b.AuthorId);

HasOne表示每本书籍对应一个作者,同时每个作者可通过书籍模型类的Author属性访问。

WithMany表示每个作者对应多本书籍,同时这些书籍可通过作者模型类的Books属性访问。

HasForeignKey用来指定书籍类的该对应关系的外键。

对于书籍和读者‘多对多’的关系:

// 书籍对读者:多对多
builder.HasMany<Reader>(b => b.Readers).WithMany(a => a.Books)
    .UsingEntity(j => j.ToTable("Book_Reader_Realation"));

HasMany表示每本书籍对应一个读者,同时这些读者可通过书籍模型类的Readers属性访问。

WithMany表示每个读者对应多本书籍,同时这些书籍可通过读者模型类的Books属性访问。

对于多对多关系,EFCore在迁移时会生成一张中间表维护多对多关系。

UsingEntity就是用来配置该中间表的设置,默认该中间表只存储两个字段,即两张表的主键,在这里为书籍表和读者表主键

读者配置类

进行了读者和读者拓展信息的‘一对一’关系的配置:

// 读者对读者拓展信息:一对一
builder.HasOne<ReaderExtern>(r => r.ReaderExtern).WithOne()
   .HasForeignKey<Reader>(r => r.ReaderExternId);

HasOne表示每个读者对应一个读者拓展信息,同时这个读者拓展信息可通过读者模型类的ReaderExtern属性访问。

WithOne表示每个读者拓展信息对应多本书籍,但WithOne中没有属性,说明无法通过读者拓展信息模型类来访问。

HasForeignKey用来指定外键所在的实体类和外键属性。注意:一对一关系必须定义外键并指定。

三、进行DbContext类的书写

internal class TestDbContext:DbContext
{

    public DbSet<Author> Authors { get; set; }
    public DbSet<Book> Books { get; set; }
    public DbSet<Reader> Readers { get; set; }
    public DbSet<ReaderExtern> ReaderExterns { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        
        string conStr = "server=localhost;port=3306;database=mytest;uid=root;pwd=123456";

        MySqlServerVersion serverVersion = new MySqlServerVersion(new Version("8.0.34"));

        optionsBuilder.UseMySql(conStr, serverVersion);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        new AuthorConfig().Configure(modelBuilder.Entity<Author>());
        new BookConfig().Configure(modelBuilder.Entity<Book>());
        new ReaderConfig().Configure(modelBuilder.Entity<Reader>());
        new ReaderExternConfig().Configure(modelBuilder.Entity<ReaderExtern>());

    }


}

首先建立一个继承DbContext类的类

然后设置多个DbSet<实体模型>的属性,作为访问这些模型类的入口,即上述代码中的Authors、Books、Readers、ReaderExterns属性。

接着重写OnConfig和OnModelCreating方法

OnConfig方法中用来配置数据库连接相关信息,包括连接字符串和使用的数据库,在这里我连接的是Mysql数据库,所以在多加一个数据库版本属性,如果连接的是Sql Server,则不需要数据库版本属性。

OnModelCreating方法在模型创建后被调用,用来配置各个模型类的配置。

四、进行数据库迁移

1、打开程序包管理控制台

​编辑

2、在程序包控制台把项目设置为类库项目

​编辑

3、执行Add-Migration指令

Add-Migration Init

Init为该次迁移的名称,可随机指定,但不可和该项目之前指定的迁移名称相同

4、执行Updata-Database指令

Update-Database

5、数据库中的表生成成功

​编辑