本文主要用于自己在使用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、数据库中的表生成成功
编辑