当前位置: 首页 > article >正文

从CRUD到高级功能:EF Core在.NET Core中全面应用(一)

目录

EFCore初识使用

EFCore增删改查

EFCore配置方式

EFCore主键操作


EFCore初识使用

        EFCore(Entity Framework Core):是.net core中一个轻量级跨平台的对象关系映射(ORM)框架,它使开发者能够通过面向对象的方式操作数据库而无需编写大量的SQL语句,支持多种数据库(如 SQL Server、SQLite、PostgreSQL、MySQL等),并且与.net core应用程序集成紧密并允许开发者更高效地处理数据访问和数据库交互。

        ORM(Object-Relational Mapping):是一种将对象与关系型数据库中的数据表及记录之间进行自动转换的技术,使得开发者能够使用面向对象的编程方式与数据库交互而无需手动编写SQL语句来执行CRUD(创建、读取、更新、删除)操作,常见的ORM如下:

EFCore:微软提供的ORM框架,适用于.net环境,支持数据库模型生成查询等多种功能,是其轻量级的跨平台版本

Dapper:优点:简单上手行为可预期性强;缺点:生产效率低需要处理底层数据库差异。

SqlSugar:非常简洁,适合快速开发和简单的CRUD操作,简单易用,快速上手

FreeSql:功能强大适合复杂场景学习曲线稍高,性能优越尤其在大数据量或复杂查询有优势

        EFCore是模型驱动(Model-Driven)的开发思想,Dapper是数据库驱动(DataBase-Driven)的开发思想的,两者没有优劣只有比较,Dapper≠性能高;EFCore≠性能差,EFCore是官方推荐推进的框架,尽量屏蔽底层数据库差异.net开发者必须熟悉,根据的项目情况再决定用哪个。

        数据库选择:EF Core是对于底层ADO.NET Core的封装,因此ADO.NET Core支持的数据库不一定被EF Core支持,但EF Core支持目前所有主流的数据库,包括Sql Server、Oracle、MySQL、PostgreSQL、SQLite等,EF Core对于Sql Server支持最完美,如果使用其他数据库只需要改动一些代码,大部分代码的用法基本不变,EF Core是尽量屏蔽底层数据库差异的,接下来博主以MySQL数据库进行举例,不了解该数据库的可以参考我之前的文章:MySQL数据库 。

        EF Core环境搭建:EF Core是一个非常强大的ORM工具,提供了简洁的API和良好的性能使得数据库操作更加直观,而且支持高级功能如异步操作和数据库迁移,让开发者能够更加专注于应用的业务逻辑而不必过多关心底层数据库的实现,接下来我们就开始进行开发环境搭建演示使用:

构建实体类:创建与数据库表进行映射的数据结构类型。

namespace test
{
    public class Book
    {
        public long Id { get; set; } // 主键
        public string Title { get; set; } // 标题
        public DateTime PublishDate { get; set; } // 出版日期
        public string Author { get; set; } // 作者
        public decimal Price { get; set; } // 价格
        public string Description { get; set; } // 描述
    }
}

创建配置类:创建实现IEntityTypeConfiguration接口的实体配置类,配置实体类和数据库表的对应关系,因为.net默认已经帮我们做好了约定大于配置,所以配置类我们其实也不需要写,但是这里还是写一下给大家了解一下:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace test
{
    class BookConfig: IEntityTypeConfiguration<Book>
    {
        public void Configure(EntityTypeBuilder<Book> builder)
        {
            builder.ToTable("T_Books");
        }
    }
}

创建DbContext:DbContext是EFCore与数据库交互的核心类,它表示数据库的一个会话并包含了DbSet属性,DbSet表示数据库中的一张表,开发者通过DbContext执行各种数据库操作(如查询、插入、更新、删除):

这里我们通过DbSet拿到我们声明的两张表的关系映射,然后通过DbContext进行数据库的连接,然后再加载我们所创建的Books和Persons类

namespace test
{
    class MyDbContext: DbContext
    {
        public DbSet<Book> Books { get; set; }
        public DbSet<Person> Persons { get; set; }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder); // 加载基类配置
            // 配置数据库连接字符串
            optionsBuilder.UseMySQL("server=localhost;database=mysql_test;user=root;password=123456"); 
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder); // 加载基类配置
            // 从程序集自动加载配置类
            modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
        }
    }
}

Migration数据库迁移:面向对象ORM开发中数据库不是程序员手动创建,而是由Migration工具生成的,关系数据库只是盛放模型数据的一个媒介而已,理想状态下程序员不用关心数据库的操作,根据对象的定义变化自动更新数据库中的表以及表结构的操作叫做Migration(迁移),迁移可以分为多步(项目进化)也可以回滚:

这里需要安装EF Core和连接MySQL的工具,版本推荐一下8.几即可,最新的可能有版本冲突:

然后我们点击工具,打开安装程序包的终端:

执行如下命令,会自动再项目的Migrations文件夹中生成操作数据库C#代码,

// init相当于git提交的日志记录的名称
add-migration init

上面该Migrations文件夹生成的文件就是操作数据库中对数据库应用的相关操作,然后我们再在终端执行如下命令,再查看数据库可以看到我们的表已经创建好了:

update-database

比如我们再在Book实体类当中再声明一些类型,然后在配置里类当中针对一些类型进行设置:

再执行上述的更新日志提交数据库的命令操作,如下所示给了我们一个警告,说数据可能会丢失,这是因为我们给一些类型限制了长度,可能会导致原本数据库当中已经存在且长度过长的数据会丢失的情况,这里我们直接忽略这个警告,直接更新数据库,可以看到我们新增的两个字段又出来了:

综上所述:EFCore通过迁移(Migration)功能,使得数据模型的变化可以同步到数据库中,迁移可以帮助跟踪数据库模式的更改并生成用于数据库更新的SQL脚本:

EFCore增删改查

根据上文我们简单的了解了一下什么是EFCore,以及如何连接数据库等简单操作,接下来我们开始讲解如何借助EFCore对数据库中的表进行增删改查操作:

插入数据:只要操作创建的实体类属性就可以向数据库中增加数据,但是通过C#代码修改实体类中的数据只是修改了内存中的数据,对实体类中数据进行修改后还需要调用DbContext的异步方法SaveChangesAsync()把修改保存到数据库当中。

也有同步的保存方法SaveChanges(),但是用EFCore都推荐用异步方法,并且EFCore默认会跟踪(Track)实体类对象以及DbSet的改变,接下来演示一下如何进行插入数据:

namespace Program
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // ctx相当于逻辑上的数据库
            using (MyDbContext ctx = new MyDbContext())
            {
                Book book = new Book(); // 创建书籍对象
                book.Title = "C#"; // 设置书籍的标题
                book.PublishDate = DateTime.Now; // 设置书籍的出版日期
                book.Author = "微软"; // 设置书籍的作者
                book.Price = 100; // 设置书籍的价格
                book.Description = "这是一本关于C#的书"; // 设置书籍的描述
                book.ImageUrl = "http://example.com/csharp.jpg"; // 设置书籍的图片URL
                book.StockQuantity = 100; // 设置书籍的库存数量
                ctx.Books.Add(book); // 将书籍对象添加到数据库上下文中
                await ctx.SaveChangesAsync(); // 保存更改到数据库中
            }
        }
    }
}

当然为了简便也可以直接在new Book实例当中书写数据,如下所示:

static async Task Main(string[] args)
{
    // ctx相当于逻辑上的数据库
    using (MyDbContext ctx = new MyDbContext())
    {
        Book b1 = new Book { Title= "Vue", PublishDate=DateTime.Now, Author="张三", Price=100, Description="没啥", ImageUrl="", StockQuantity=1 };
        Book b2 = new Book { Title= "React", PublishDate=DateTime.Now, Author="李四", Price=100, Description="没啥", ImageUrl="", StockQuantity=1 };
        Book b3 = new Book { Title= "Angular", PublishDate=DateTime.Now, Author="王五", Price=100, Description="没啥", ImageUrl="", StockQuantity=1 };
        Book b4 = new Book { Title= "Node", PublishDate=DateTime.Now, Author="赵六", Price=100, Description="没啥", ImageUrl="", StockQuantity=1 };
        ctx.Books.Add(b1); 
        ctx.Books.Add(b2);
        ctx.Books.Add(b3);
        ctx.Books.Add(b4);
        await ctx.SaveChangesAsync(); 
    }
}

查询数据:DbSet实现了IEnumerable<T>接口,因此可以对DbSet实现Linq操作来进行数据查询,EF Core会把Linq操作转换为SQL语句,面向对象而不是面向数据库:

如下是Linq查询的简单操作,详情可以参考:LINQ操作 :

namespace Program
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // ctx相当于逻辑上的数据库
            using (MyDbContext ctx = new MyDbContext())
            {
                var books = ctx.Books.Where(b => b.Title == "Vue" || b.Title == "React");
                foreach (var b in books)
                {
                    Console.WriteLine($"Title={b.Title}, PublishDate={b.PublishDate}, Price={b.Price}");
                }
            }
        }
    }
}

修改数据:要对数据进行修改,首先需要把要修改的数据查询出来, 然后再对查询出来的对象进行修改,然后再执行SaveChangesAsync()保存修改,如下所示:

namespace Program
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // ctx相当于逻辑上的数据库
            using (MyDbContext ctx = new MyDbContext())
            {
                var books = ctx.Books.Single(b => b.Title == "Vue" );
                Console.WriteLine(books.Price);
                books.Price = 80;
                await ctx.SaveChangesAsync();
            }
        }
    }
}

删除数据:删除需要把要删除的数据查询出来,然后再调用DbSet或者DbContext的Remove方法把对象删除,然后再执行SaveChangesAsync(保存修改,如下所示:

namespace Program
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // ctx相当于逻辑上的数据库
            using (MyDbContext ctx = new MyDbContext())
            {
                var books = ctx.Books.Single(b => b.Id == 4 );
                Console.WriteLine(books.Title);
                ctx.Books.Remove(books);
                await ctx.SaveChangesAsync();
            }
        }
    }
}

EFCore配置方式

对于EFCore中的配置类,上文EFCore初识使用中已经讲解到了,其实.net core已经默认给我们进行了约定配置,根据我们设置的属性进行自动判断,其约定配置规则如下所示:

1)表名采用DbContext中的对应的DbSet的属性名

2)数据表列的名字采用实体类属性的名字,列的数据类型采用和实体类属性类型最兼容类型

3)数据表列的可空性取决于对应实体类属性的可空性

4)名字为Id的属性为主键,如果主键为short,int或者long类型则默认采用自增字段,如果主键为Guid类型则默认采用的Guid生成机制生成主键值。

这里简单介绍一下对于EFCore中配置类的一些相关操作使用,其主要分为以下两种方式,对于大部分功能重叠的项目,两种方式可以混用但是不建议混用:

FluentAPI:优点是解耦缺点是复杂,其主要是把配置写到单独的配置类当中,主要通过如下方式进行使用:

namespace test
{
    class BookConfig: IEntityTypeConfiguration<Book>
    {
        public void Configure(EntityTypeBuilder<Book> builder)
        {
            builder.ToTable("T_Books");
            builder.Property(b => b.Title).HasMaxLength(50).IsRequired();
        }
    }
}

Data Annotation:优点是简单缺点是耦合,其主要是把配置以特性的形式标注到实体类中,如下所示:

namespace test
{
    [Table("T_Students")]
    class Student
    {
        public int Id { get; set; } // 主键
        [Required(ErrorMessage = "姓名不能为空")]
        [MaxLength(10, ErrorMessage = "姓名长度不能超过{10}")]
        public string Name { get; set; } // 学生姓名
    }
}

EFCore主键操作

EF Core支持多种主键生成策略:自动增长、Guid、Hi/Lo算法等,具体如下所示:

自动增长:优点简单;缺点数据库迁移以及分布式系统中比较麻烦并发性能差。

long、int等类型主键默认是自增,因为是数据库生成的值,所以SaveChanges后会自动把主键的值更新到Id属性,例如插入帖子后,自动重定向帖子地址,自增字段的代码中不能为Id赋值必须保持默认值0,否则运行的时候就会报错。

如下插入数据之前默认是0,插入到数据之后,其Id会自动增长得到的结果就是6了:

Guid:也叫UUID算法,生成一个全局唯一的Id,适合于分布式系统,在进行多数据库数据合并的时候很简单,优点:简单高并发,全局唯一;缺点:磁盘空间占用大。

        Guid值不连续,使用Guid类型做主键的时候不能把主键设置为聚集索引,因为聚集索引是按照顺序保存主键的,因此用Guid做主键性能差,比如MySQL的InnoDB引擎中主键是强制使用聚集索引的,有的数据库支持部分的连续Guid,比如SQLServer中的NewSequentialIdO,但也不能解决问题。在SQLServer等中不要把Guid主键设置为聚集索引;在MySQL中插入频繁的表不要用Guid做主键,示例如下:

Hi/Lo算法:EF Core支持Hi/Lo算法来优化自增列了,主键值由两部分组成:高位(Hi)和低位(Lo),高位由数据库生成,两个高位之间间隔若干个值,由程序在本地生成低位,低位的值在本地自增生成,不同进程或者集群中不同服务器获取的Hi值不会重复,而本地进程计算的Lo则可以保证可以在本地高效率的生成主键值,但是HLo算法不是EF Core的标准。


http://www.kler.cn/a/467382.html

相关文章:

  • HarmonyOS:@Builder装饰器:自定义构建函数
  • 字玩FontPlayer开发笔记6 Tauri2设置菜单
  • Leetcode打卡:设计一个ATM机器
  • CSP初赛知识学习计划(第三天)
  • 解决Linux切换用户后的命令提示符为-bashxx$的问题
  • 若依修改超级管理员admin的密码
  • STM32-笔记26-WWDG窗口看门狗
  • 基于高斯噪声模型的信号去噪——图像相加去噪的理论保证
  • 22408操作系统期末速成/复习(考研0基础上手)
  • 前端开发语言涉及到 的集合框架(Collections Framework)
  • PHP在做api开发中,RSA加密签名算法如何使用 ?
  • Fabric环境部署-Git和Node安装
  • 蛋白互作组学系列丨(四)IP-MS数据分析
  • 基础图形化界面的一个图片爬虫期末
  • 【初阶数据结构与算法】排序算法总结篇(每个小节后面有源码)(直接插入、希尔、选择、堆、冒泡、快速、归并、计数以及非递归快速、归并排序)
  • uniapp【拨打电话,发送消息】
  • 什么是TDD测试驱动开发(Test Driven Development)?
  • 【架构设计(一)】常见的Java架构模式
  • PDF阅读和编辑工具——xodo
  • 免费下载 | 2024中国大数据产业白皮书
  • MYSQL----------字符集
  • 一文讲清楚HTTP常见的请求头和应用
  • opencv与halcon的差距及改进方法
  • 浅谈文本匹配
  • 深入Android架构(从线程到AIDL)_10 主线程(UI 线程)的角色
  • [Day 12]904.水果成篮