EFCore的一些知识点:
-
ORM
Object Relational Mapping:对象关系映射,让开发者可以用对象的形式操作关系型数据库,在代码中类对象的形式组织数据,通过ORM的内置方法,ORM引擎通过把对象和代码逻辑转化为相应的SQL语句,对数据库进行增删改查;
比如插入数据库的伪代码如下:
//插入例子
Book book = new Book(){id=1, title="明朝那些事儿",author="当年明月",price=20};
orm.Save(book);
//查询例子
Book b = orm.Books.FirstOrDefault(b=>b.price > 15);
Console.Write(b.tile);
-
RDB 关系型数据库 有Oracle、MS SQLserver、Mysql等;
-
常用的ORM:EF Core、Dapper、SqlSugar、FreeSql等;
-
EF Core 与其它ORM比较
- Entity Framework Core(EF Core)是微软官方的ORM框架:优点:功能强大、官方支持、生产效率高、力求屏蔽底层数据库差异; 缺点:复杂、上手门槛高。
- Dapper。优点:简单,几分钟即可上手,行为可预期性强;缺点:生产效率低,需要处理底层数据库差异。
- EF Core是模型驱动(Model-Driven)的开发思想,Dapper是数据库驱动(DataBase-Driven)的开发思想的。没有优劣,只有比较。
- 性能:Dapper等≠性能高;EFCore≠性能差。
- EF Core是官方推荐、推进的框架,尽量屏蔽底层数据库差异,.NET开发者必须熟悉,根据的项目情况再决定用哪个。
-
4.EFCore 与 EF 比较
- EF有DB First、ModelFirst、Code First。 EF Core不支持模型优先,推荐使用代码优先,遗留系统可以使用Scaffold-DbContext来生成代码实现类似DBFirst的效果,但是推荐用Code First .
- EF会对实体上的标注做校验,EFCore追求轻量化,不校验
- 熟悉EF的话,掌握EFCore会很容易,很多用法都移植过来了,EF Core又增加了很多新东西。
- EF中的一些类的命名空间以及一些方法的名字在EF Core中稍有不同。
EF Core环境搭建
-
关于EFCore支持的数据库
EF Core框架是对于底层ADO.NET Core的封装,ADO.NET Core支持的数据库不一定被EF Core支持,EF Core支持所有主流的数据库,包括MS SqlServer、Oracle、MySQL、PostgresOL、Sqlite等。可以自己实现Provider支持其他数据库。EFCore 对于SqlServer支持最完美,毕竟是微软自己的产品,MySql、PostgreSql也不错。这三者是.NET圈中用的最多的三个。相对于其他ORM框架,ECore更能屏蔽底层数据库差异。
-
开发环境的搭建
1.步骤:
2.代码演示
新建一个Book实体类
public class Book
{
public long Id { get; set; }
public string Title { get; set; }
public DateTime PubTime { get; set; }
public double Price { get; set; }
public string Author { get; set; }
public override string ToString()
{
return $"书名:{Title},发布时间:{PubTime},价格:{Price},作者:{Author}";
}
}
别忘记了NuGet安装相应的包:
创建实现了IEntityTypeConfiguration接口的实体配置类,配置实体类和数据库表的对应关系,可以定义表名,主键,字段长度,字段非空等设置。
public class BookConfig : IEntityTypeConfiguration<Book>
{
public void Configure(EntityTypeBuilder<Book> builder)
{
//创建表
builder.ToTable("T_Books");
//设置字段长度,和非空限制
builder.Property(e=>e.Title).HasMaxLength(100).IsRequired();
builder.Property(e => e.Author).HasMaxLength(100).IsRequired(false);
}
}
创建继承自DbContext的类,此类中会定义数据库的链接,数据库有哪些表的实体类等:新增的实体类作为DBContext类中的一个属性,类型为DbSet<T>;
在OnConfiguring方法中定义数据库的链接,在OnModelCreating方法中去获取所有的继承了IEntityTypeConfiguration接口的配置类,我这里是通过程序集的方式获取;
class MyDBContext:DbContext
{
public DbSet<Book> Books { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
string ConnectionString = "Database=test;Server=127.0.0.1;User Id=root;Password=123456";
optionsBuilder.UseMySql(ConnectionString, new MySqlServerVersion(new Version(8, 0, 39)));
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//从当前程序集中加载所有继承了IEntityTypeConfiguration的类
modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
}
}
-
数据库迁移:
在模型驱动的ORM开发中,数据库不是程序员手动创建的,而是由Migration工具生成的。关系数据库只是装载模型数据的一个媒介而已, 根据实体类对象的定义变化,自动更新数据库中的表以及表结构的操作,叫做Migration(迁移),迁移可以分为多步(项目进化),也可以回滚
Add-Migration命令,是执行数据库迁移的命令,该命令需要建立在安装了Microsoft.Entityframework.Tools的包基础上才能执行,
在“程序包管理器控制台”中执行如下命令Add-Migration InitialCreate会自动在项目的migrations文件夹中生成操作数据库的C#代码。Add-Migration后面跟的参数是为本次数据库变动取一个名字方便后期跟踪。
代码需要执行后才会应用对数据库的操作。“程序包管理器控制台”中执行Update-database。最终才会在数据库中生成对应的表;
执行命令成功后,会在项目的Migration目录下生成一个对应的操作日志类,里面记录了本次操作的详细信息,包括新建表,新建字段的一些逻辑代码
上面一步只是通过EFCore生成待执行的数据库命令代码,真正变更脚本到数据库中还需要执行update-database命令,才会看到数据库的变化
可以看到,数据库中已经产生了对应的表,并且字段也是我们在实体类中定义的名字类型等
-
修改表结构
- 项目开发中,根据需要,可能会在已有实体中修改、新增、删除表、列等。
- 想要限制Title的最大长度为200,Title字段设置为“不可为空”,并且想增加一个不可为空且最大长度为100的PublishUnit(出版社)属性。首先在Book实体类中增加一个PublishUnit属性
- 修改BookEntityConfigbuilder.ToTable('T Books");
builder.Property(e =>e.Title).HasMaxLength(200).IsRequired();
builder.Property(e =>e.PublishUnit).HasMaxLength(100).IsRequired();
- 执行Add-Migration AddPublishUnit_ModifyTitle(执行过程中提示数据可能会丢失,因为修改了字段长度,如果确定没问题,就执行下一步)
- Update-Database
执行完update-database命令后,我们刷新数据库查看变化:可以看到,Title字段的长度已经变为200,表里面也新增加了PublishUnit字段 Config类中还有一些其他的方法来修改表数据,比如设置主键,添加索引等等;
下面这张图就体现了一个完整的模型驱动数据的流程图
EF Core 对数据增删改查
-
数据插入
利用DBContext对象进行数据的新增(对数据库的操作,我们这里尽量用异步的方法)
static async Task Main(string[] args)
{
using (MyDBContext dBContext = new MyDBContext())
{
//插入
Book book = new Book()
{
Author = "yanchan",
Price = 12.8,
PubTime = DateTime.Now,
Title = "人生一串",
PublishUnit="西南文艺出版社"
};
await dBContext.Books.AddAsync(book);//把book对象加入Books这个逻辑的表里面
await dBContext.SaveChangesAsync();//update-database
}
}
可以看到,数据库表里面已经插进去数据了
-
数据查询
从DbSet<T>的内部定义来看,它是实现了IEnumerable接口的,那么我们就可以用Linq查询语句来对数据进行查询
1.例如,我需要查询价格高于6元的书籍
static async Task Main(string[] args)
{
using (MyDBContext dBContext = new MyDBContext())
{
//查询
IQueryable<Book> books = dBContext.Books.Where(b => b.Price > 6);
foreach (var book in books)
{
Console.WriteLine(book.ToString());
}
}
}
运行后查看结果如下:
2.或者我需要查询作者为liyumin的书籍
static async Task Main(string[] args)
{
using (MyDBContext dBContext = new MyDBContext())
{
//查询
IQueryable<Book> books = dBContext.Books.Where(b => b.Author == "liyumin");
foreach (var book in books)
{
Console.WriteLine(book.ToString());
}
}
}
查询的输出结果:
-
数据删除或修改
- 要对数据进行修改,首先要把修改的数据查询出来,然后再对查询出来的对象进行修改,然后再执行SaveChangesAsync()保存修改。
- 删除也是先把要删除的数据查询出来,然后再调用Dbset或者DbContext的Remove方法把对象删除,然后再执行SaveChangesasync()保存修改
假如我们需要把Id为4的书的作者改为Tom,然后把作者为liyumin的书籍删除掉:
static async Task Main(string[] args)
{
using (MyDBContext dBContext = new MyDBContext())
{
//更新
var bookUpdate = dBContext.Books.Single(b => b.Id == 4);
bookUpdate.Author = "Tom";
//删除
var bookDelete = dBContext.Books.Where(b => b.Author=="liyumin");
dBContext.Books.RemoveRange(bookDelete);
//最后需要执行SaveChangeAsync()
await dBContext.SaveChangesAsync();
}
}
执行程序之后查看数据,可以看到ID为4的书的作者已经被修改为Tom,作者为liyumin的书籍已经被删除掉了