什么是ORM?

ORM(Object Relational Mapping).让开发者用对象操作的形式操作数据库。
常见的ORM:EF Core\Dapper\SqlSugar\FreeSql

EF Core

image.png
image.png
image.png

搭建EF Core开发环境

  • 建实体类
  • 引入Nuget包:Microsoft.EntityFrameworkCore.SqlServer
  • 建配置类,配置类实现IEntityTypeConfiguration接口,T是实体类

image.png
image.png
image.png
如果没有配置类TestDbContext则会生成一个默认的表名,表名为属性名。
image.png

  1. using EFCoreRelation.Models;
  2. using Microsoft.EntityFrameworkCore;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. namespace EFCoreRelation.DbContexts
  9. {
  10. public class AlbertDbContext:DbContext
  11. {
  12. public DbSet<Article> Articles;
  13. public DbSet<Comment> Comments;
  14. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  15. {
  16. optionsBuilder.UseSqlServer(connectionString: "Server=.;Database=albertefcore;" +
  17. "Trusted_Connection=True;MultipleActiveResultSets=true");
  18. }
  19. protected override void OnModelCreating(ModelBuilder modelBuilder)
  20. {
  21. base.OnModelCreating(modelBuilder);
  22. modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
  23. }
  24. }
  25. }

image.png

Migration数据库迁移概念

image.png

使用Migration工具将类生成Table

  • Nuget安装Microsoft.EntityFrameworkCore.Tools
  • 执行Add-Migration InitialCreate(这是注释) 相当于每次提交,可进行回滚至某个操作
  • update-datebase

image.png
image.png

Migration迁移警告

An operation was scaffolded that may result in the loss of data. Please review the migration for accuracy.是因为有些column设置了长度限制,可能会造成数据截断问题。
image.png

深入研究Migrations

image.png
Update-Database “Add Cat by attribute”回滚版本名称加引号
如果要删除迁移版本,将项目工程中的版本.cs删除,将SnapShot.cs中生成Entity的删除,数据库中对应的历史版本记录也删掉。
Remove-migration
image.png
Remove-migration 每次向上删除一个版本
Update-Database只适合更新开发服务器,不适合生产环境(开发人员不可以直接连接生产数据库)
Script-Migration生成Sql执行脚本
image.png
Add-Migration -OutputDir来指定不同的数据库生成脚本
image.png

修改表结构

image.png
image.png

使用EFCore

插入数据

image.png

查询数据—复习Linq

image.png
image.png

修改、删除数据

image.png

源代码演示

  1. using Microsoft.EntityFrameworkCore;
  2. using System;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. namespace _210917_Demon01_EFCoreAlbert
  6. {
  7. internal class Program
  8. {
  9. /// <summary>
  10. /// <see cref="InitDataBase(DbContext)"/>
  11. /// </summary>
  12. /// <param name="args"></param>
  13. /// <returns></returns>
  14. static async Task Main(string[] args)
  15. {
  16. //插入数据 ctx=逻辑上的数据库
  17. using (var ctx = new MyDbContext())
  18. {
  19. var booksTable = ctx.Books;
  20. foreach (var item in booksTable)
  21. {
  22. ctx.Remove(item);
  23. }
  24. await ctx.SaveChangesAsync();
  25. //初始化数据表
  26. await InitDataBase(ctx);
  27. //查询
  28. var books = ctx.Books.Where(e => e.Price > 80);
  29. foreach (var item in books)
  30. {
  31. Console.WriteLine(item.Title);
  32. }
  33. //查询是否存在一本叫Simple algorithm的书籍,如果存在则打印出作者名字
  34. var book = ctx.Books.Single(e => e.Title == "Simple algorithm");
  35. Console.WriteLine(book.AuthorName);
  36. books = ctx.Books.OrderBy(e => e.Price);
  37. foreach (var item in books)
  38. {
  39. Console.WriteLine(item.Title);
  40. }
  41. //通过分组来取每一个作者的书数量和最大价格
  42. var groups = ctx.Books.GroupBy(e => e.AuthorName).Select(g => new
  43. {
  44. Name = g.Key,
  45. BooksCount = g.Count(),
  46. MaxPrice = g.Max(e => e.Price)
  47. });
  48. foreach (var item in groups)
  49. {
  50. Console.WriteLine($"Name:{item.Name}==" +
  51. $"BooksCount:{item.BooksCount}==" +
  52. $"MaxPrice:{item.MaxPrice}.");
  53. }
  54. //修改数据,albert作者的书籍的价格调高
  55. var albertBooks = ctx.Books.Where(e => e.AuthorName == "AlbertZhao");
  56. foreach (var item in albertBooks)
  57. {
  58. item.Price = 198;
  59. }
  60. //删除书籍Top of the ware
  61. var cBook = ctx.Books.Single(e => e.Title == "Top of the ware");
  62. ctx.Remove(cBook);
  63. await ctx.SaveChangesAsync();
  64. }
  65. }
  66. /// <summary>
  67. ///
  68. /// </summary>
  69. /// <remarks>Init DataBase</remarks>
  70. /// <param name="ctx"></param>
  71. /// <returns></returns>
  72. static async Task InitDataBase(DbContext ctx)
  73. {
  74. Book b1 = new Book()
  75. {
  76. AuthorName = "AlbertZhao",
  77. Title = "Simple algorithm",
  78. Price = 99,
  79. PubTime = new DateTime(2022, 12, 1)
  80. };
  81. Book b2 = new Book()
  82. {
  83. AuthorName = "ZackYang",
  84. Title = "Zero-Based fun learning C",
  85. Price = 59.8,
  86. PubTime = new DateTime(2019, 3, 1)
  87. };
  88. Book b3 = new Book()
  89. {
  90. AuthorName = "WuJun",
  91. Title = "The beauty of math",
  92. Price = 99,
  93. PubTime = new DateTime(2018, 1, 1)
  94. };
  95. Book b4 = new Book()
  96. {
  97. AuthorName = "WuJun",
  98. Title = "Top of the ware",
  99. Price = 198,
  100. PubTime = new DateTime(2021, 1, 1)
  101. };
  102. Book b5 = new Book()
  103. {
  104. AuthorName = "Liangtongming",
  105. Title = "In-depth upderstanding of asp.net core",
  106. Price = 169,
  107. PubTime = new DateTime(2021, 1, 1)
  108. };
  109. //将对象数据添加到内存逻辑的数据表中
  110. await ctx.AddAsync(b1);
  111. await ctx.AddAsync(b2);
  112. await ctx.AddAsync(b3);
  113. await ctx.AddAsync(b4);
  114. await ctx.AddAsync(b5);
  115. //将内存中的数据同步到数据库里
  116. await ctx.SaveChangesAsync();
  117. }
  118. }
  119. }

批量删除、修改(Zack.EFCore.Batch.MSSQL开源包)

引用杨中科老师的包-Zack.EFCore.Batch(使用文档:https://github.com/yangzhongke/Zack.EFCore.Batch/blob/main/README_CN.md
或者使用Z.EntityFramework.Extensions
image.png

  1. MyDbContext :DbContext
  2. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  3. {
  4. base.OnConfiguring(optionsBuilder);
  5. string connStr = "Server = .; Database = AlbertBook; Trusted_Connection = True;MultipleActiveResultSets=true";
  6. optionsBuilder.UseSqlServer(connStr);
  7. optionsBuilder.UseBatchEF_MSSQL();
  8. }
  9. Main()
  10. //使用杨中科老师的Nuget包:Zack.EFCore.Batch
  11. await ctx.DeleteRangeAsync<Book>(e => e.Price > 80 && e.AuthorName == "WuJun");
  12. await ctx.BatchUpdate<Book>()
  13. .Set(b => b.Price, b => b.Price + 3)
  14. .Set(b => b.Title, b => "HelloWorld")
  15. .Set(b => b.AuthorName, b => b.Title.Substring(3, 2) + b.AuthorName.ToUpper())
  16. .Set(b => b.PubTime, b => DateTime.Now)
  17. .Where(b => b.Id > 1 || b.AuthorName.StartsWith("Albert"))
  18. .ExecuteAsync();
  19. //批量插入数据,一个list直接搞定
  20. List<Book> books = new List<Book>();
  21. for (int i = 0; i < 100; i++)
  22. {
  23. books.Add(new Book { AuthorName = "abc" + i, Price = new Random().NextDouble(), PubTime = DateTime.Now, Title = Guid.NewGuid().ToString() });
  24. }
  25. using (TestDbContext ctx = new TestDbContext())
  26. {
  27. ctx.BulkInsert(books);
  28. }

Sql Server小工具—Sql Server Profiler查询所有接收的sql

image.png

EFCore实体的配置

image.png
特性配置和FluentAPI两种方式查询网址:https://docs.microsoft.com/en-us/ef/core/
image.png
1632991606(1).png
image.png
image.png

主键不是小事

自增主键是由数据库赋值,Guid主键是由EFCore引擎赋值。
雪花算法,分布式唯一ID解决方案 https://cloud.tencent.com/developer/article/1772047
image.png
使用Guid作为主键的时候,不要把主键设置为聚集索引,否则插入效率特别低,要进行顺序重排,效率极其低下。
SqlServer中不要把Guid主键设置为聚集索引,在mysql中插入频繁的表不要用Guid做主键。
MySql和MongoDb少用Guid做主键。
image.png
雪花算法
Hi/Lo算法 本地向服务器要一组Hi值,例如600-700,服务器一次性给你一组,等本地用完再向服务器要的时候,可能下次给到的就是800-900了。
image.png

EFCore反向工程-从数据库生成实体类—不推荐使用这种方法,直接使用第三方工具

三种建模方式:DBFirst数据库优先(数据库先建好) ModelFirst模型优先(图形化先建好) CodeFirst代码优先
已经存在表了,想利用反向工程将存在的表反向生成。

  1. 新建项目,将引用的包拷贝到新项目中,设置启动项目为当前项目

    1. <ItemGroup>
    2. <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.11" />
    3. <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.11">
    4. <PrivateAssets>all</PrivateAssets>
    5. <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    6. </PackageReference>
    7. </ItemGroup>
  2. 执行以下命令,这个命令Scaffold-DbContext 数据库连接字符串 数据库类型

    1. Scaffold-DbContext "Server = .; Database = AlbertBook; Trusted_Connection = True;MultipleActiveResultSets=true" Microsoft.EntityFrameworkCore.SqlServer
  3. 如果新建了一个表,需要强制覆盖,在最后加上-force

    1. Scaffold-DbContext "Server = .; Database = AlbertBook; Trusted_Connection = True;MultipleActiveResultSets=true" Microsoft.EntityFrameworkCore.SqlServer -Force

image.png
image.png

EFCore底层如何操作数据库 EFCore和ADO.NET合作关系

YZK:框架是帮助程序员简化工作的,不是把程序员变成傻瓜的
应用程序—>ADO.NET Core(SQL)—>数据库 EFCore底层依旧是ADO.NET Core
image.png
image.png
image.png

EFCore有那些做不到的事

image.png
AST抽象语法树
image.png

三种方法通过代码查看SQL语句

标准日志

image.png

  1. //在DbContext继承类中输入以下代码
  2. private static readonly ILoggerFactory loggerFactory = LoggerFactory.Create(builder=>builder.AddConsole());
  3. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  4. {
  5. if (!optionsBuilder.IsConfigured)
  6. {
  7. #warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
  8. optionsBuilder.UseSqlServer("Server = .; Database = AlbertBook; Trusted_Connection = True;MultipleActiveResultSets=true");
  9. optionsBuilder.UseLoggerFactory(loggerFactory);
  10. }
  11. }
  12. //在Program.cs中输入以下代码
  13. static void Main(string[] args)
  14. {
  15. using (var ctx = new AlbertBookContext())
  16. {
  17. var books = ctx.TBooks.OrderBy(e => e.Price);
  18. foreach (var item in books)
  19. {
  20. System.Console.WriteLine(item.Title);
  21. }
  22. }
  23. }

简单日志

  1. optionsBuilder.LogTo(msg =>
  2. {
  3. //msg是ef输出的消息
  4. Console.WriteLine(msg);
  5. });

image.png

ToQueryString

We need using Microsoft.EntityFrameworkCore

  1. //We need execute var ctx = new AlbertBookContext:DbContext
  2. //Console.WriteLine(books.ToQueryString())
  3. static void Main(string[] args)
  4. {
  5. using (var ctx = new AlbertBookContext())
  6. {
  7. var books = ctx.TBooks.OrderBy(e => e.Price);
  8. foreach (var item in books)
  9. {
  10. System.Console.WriteLine(item.Title);
  11. }
  12. string sqlServerOrderByPrice = books.ToQueryString();
  13. System.Console.WriteLine(sqlServerOrderByPrice);
  14. }
  15. }

image.png

三种方式总结

image.png

EFCore-同样的LINQ被翻译为不同的SQL语句

SQLServer

EF Core对于SqlServer的翻译结果:

  1. C#:
  2. var persons = ctx.TPeople.Where(p => p.BirthDay.Value.Year == 1998).Take(3);
  3. SQL Server:
  4. SELECT TOP(@__p_0) [t].[Id], [t].[Age], [t].[BirthDay], [t].[BirthPlace], [t].[Name], [t].[Salary]
  5. FROM [T_Person] AS [t]
  6. WHERE DATEPART(year, [t].[BirthDay]) = 1998

image.png

MySQL

  1. Add Nuget.org
  2. 增加第二个Context继承类,单独和SQLServer的分开,例如SQLServer_AlbertContext.cs MySQL_AlbertContext.cs
  3. Add-Migration InitialCreate -Context MySQL_AlbertContext -OutputDir Migrations\MySqlMigrations 这句话注释为InitialCreate 执行的Context类为MySQL继承的Context类
    输出到Migrations\MySqlMigrations文件夹下面 相关的Link:https://docs.microsoft.com/zh-cn/ef/core/managing-schemas/migrations/providers?tabs=vs
  4. 执行数据库更新操作,Update-Database -context mysql_albertbookcontext
  5. 刷新Navicat执行完毕。

image.png
image.png
image.png

PostgreSql—微软官方准官方的包,EFCore的开发人员

image.png