Bootstrap

.Net Core 核心知识点(七)--认识EF Core(1)

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比较
  1. Entity Framework Core(EF Core)是微软官方的ORM框架:优点:功能强大、官方支持、生产效率高、力求屏蔽底层数据库差异; 缺点:复杂、上手门槛高。
  2. Dapper。优点:简单,几分钟即可上手,行为可预期性强;缺点:生产效率低,需要处理底层数据库差异。
  3. EF Core是模型驱动(Model-Driven)的开发思想,Dapper是数据库驱动(DataBase-Driven)的开发思想的。没有优劣,只有比较。
  4. 性能:Dapper等≠性能高;EFCore≠性能差。
  5. EF Core是官方推荐、推进的框架,尽量屏蔽底层数据库差异,.NET开发者必须熟悉,根据的项目情况再决定用哪个。
  • 4.EFCore 与 EF 比较
  1. EF有DB First、ModelFirst、Code First。 EF Core不支持模型优先,推荐使用代码优先,遗留系统可以使用Scaffold-DbContext来生成代码实现类似DBFirst的效果,但是推荐用Code First .
  2. EF会对实体上的标注做校验,EFCore追求轻量化,不校验
  3. 熟悉EF的话,掌握EFCore会很容易,很多用法都移植过来了,EF Core又增加了很多新东西。
  4. 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命令,才会看到数据库的变化

可以看到,数据库中已经产生了对应的表,并且字段也是我们在实体类中定义的名字类型等

  • 修改表结构
  1. 项目开发中,根据需要,可能会在已有实体中修改、新增、删除表、列等。
  2. 想要限制Title的最大长度为200,Title字段设置为“不可为空”,并且想增加一个不可为空且最大长度为100的PublishUnit(出版社)属性。首先在Book实体类中增加一个PublishUnit属性
  3. 修改BookEntityConfigbuilder.ToTable('T Books");

builder.Property(e =>e.Title).HasMaxLength(200).IsRequired();

builder.Property(e =>e.PublishUnit).HasMaxLength(100).IsRequired();

  1. 执行Add-Migration AddPublishUnit_ModifyTitle(执行过程中提示数据可能会丢失,因为修改了字段长度,如果确定没问题,就执行下一步)
  2. 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());
                }
            }
        }

查询的输出结果:

  • 数据删除或修改
  1. 要对数据进行修改,首先要把修改的数据查询出来,然后再对查询出来的对象进行修改,然后再执行SaveChangesAsync()保存修改。
  2. 删除也是先把要删除的数据查询出来,然后再调用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的书籍已经被删除掉了

;