一. 新功能(变化)
前置
参考官方文档:https://docs.microsoft.com/zh-cn/ef/core/what-is-new/ef-core-5.0/whatsnew
https://docs.microsoft.com/zh-cn/ef/core/what-is-new/ef-core-5.0/breaking-changes
映射实体:Scaffold-DbContext "Server=.;Database=EFDB01;User ID=sa;Password=123456;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Entity -UseDatabaseNames -DataAnnotations -NoPluralize
1. 直接将查询语句输出到控制台
在OnConfiguring中直接配置 optionsBuilder.LogTo(Console.WriteLine); 详见CoreConsole
PS:有点鸡肋,不如之前写法好用。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.LogTo(Console.WriteLine); }
2. 灵活的实体映射
实体类型通常映射到表或视图,以便 EF Core 在查询该类型时将拉回表或视图的内容。 EF Core 5.0 添加了其他映射选项, 其中可将实体映射到 SQL 查询(称为“定义查询”)或表值函数 (TVF):
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Post>().ToSqlQuery( @"SELECT Id, Name, Category, BlogId FROM posts UNION ALL SELECT Id, Name, ""Legacy"", BlogId from legacy_posts"); modelBuilder.Entity<Blog>().ToFunction("BlogsReturningFunction"); } -------- protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Blog>().ToTable("Blogs").ToView("BlogsView"); }
3. DbContextFactory 和 PooledDbContextFactory
EF Core 5.0 引入了 AddDbContextFactory 和 AddPooledDbContextFactory 来注册工厂,以便在应用程序的依赖项注入 (D.I.) 容器中创建 DbContext 实例;
注:当应用程序代码需要手动创建和处理上下文实例时,这很有用。
代码分享:
public void ConfigureServices(IServiceCollection services) { //工厂注入 services.AddDbContextFactory<EFDB01Context>(b => b.UseSqlServer(Configuration.GetConnectionString("EFStr"))); //services.AddPooledDbContextFactory<EFDB01Context>(b => b.UseSqlServer(Configuration.GetConnectionString("EFStr"))); services.AddControllersWithViews(); }
使用:
/// <summary> /// 测试 DbContextFactory 注入,然后手动创建上下文实例 /// </summary> public class Test1Controller : Controller { private readonly IDbContextFactory<EFDB01Context> _contextFactory; public Test1Controller(IDbContextFactory<EFDB01Context> contextFactory) => _contextFactory = contextFactory; public IActionResult Index() { using (var context = _contextFactory.CreateDbContext()) { var data = context.T_UserInfor.ToList(); } return View(); } }
注: 需要把上下文中的无参构造函数去掉。
4. 数据库排序规则
EF Core 5.0 现支持在数据库、列或查询级别指定文本排序规则。 这样就可以灵活但不影响查询性能的方式配置大小写区分和其他文本方面。
eg:modelBuilder.Entity<User>().Property(e => e.Name).UseCollation("SQL_Latin1_General_CP1_CS_AS");
5. IndexBuilder.HasName 现已过时
之前:只能在给定的一组属性上定义一个索引。 索引的数据库名称是使用 IndexBuilder.HasName 配置的。
现在:允许在同一组属性上使用多个索引。 这些索引现在可以通过模型中的名称来区分。如:[Index(nameof(userId), Name = "idx_userId")] 按照约定,模型名称将用作数据库名称;
不过,也可以使用 HasDatabaseName 单独进行配置。
6. 支持SQLServer的DataLength函数
var count = db.T_UserInfor.Count(u => 10 < EF.Functions.DataLength(u.addTime));
翻译的SQL语句:
SELECT COUNT(*) FROM [T_UserInfor] AS[t] WHERE 10 < DATALENGTH([t].[addTime])
7. 反向工程的模型的默认为复数
不想保留负数的话,需要加 -NoPluralize
eg:
Scaffold-DbContext "Server=.;Database=EFDB01;User ID=sa;Password=123456;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Entity -UseDatabaseNames -DataAnnotations -NoPluralize
8. 其他
(1). EF Core 5.0 不支持 .NET Framework
(2). IProperty.GetColumnName() 现已过时
(3). GetPropertyName 和 SetPropertyName 已重命名, 改为:GetJsonPropertyName、SetJsonPropertyName
9. 补充
支持多对多,无需显式映射联接表
当查询使用 Include 或投影来返回多个相关集合时,支持拆分查询
每个类型一张表 (TPT) 映射
共享型实体类型和属性包
必需的一对一依赖项
重新生成 SQLite 表
公开了事件计数器
二. EFCore3.x-5.x性能测试
1. 环境说明
(1). 数据库:SQLServer2014 、MySQL5.7
(2). EF版本:EFCore3.1.8 、 EFCore 5.0.3、Pomelo.EntityFrameworkCore.MySql 3.2.4、Pomelo.EntityFrameworkCore.MySql 5.0.0-alpha.2
(3). 其它说明: 均是基于单表操作,修改 和 删除都要先查询后操作。
2. 测试结果
(1). SQLServer
(2). MySQL
3. 代码分享
{ using (EFDB01Context db = new EFDB01Context()) { Stopwatch watch = new Stopwatch(); watch.Start(); #region 新增-Add //{ // for (int i = 0; i < 40000; i++) // { // T_UserInfor user = new T_UserInfor(); // user.id = Guid.NewGuid().ToString("N"); // user.userName = $"ypf{i}"; // user.userSex = $"男{i}"; // user.userAge = i; // user.addTime = DateTime.Now; // db.Add(user); // } // int count = db.SaveChanges(); //} #endregion #region 新增-AddRange //{ // List<T_UserInfor> list = new List<T_UserInfor>(); // for (int i = 0; i < 100000; i++) // { // T_UserInfor user = new T_UserInfor(); // user.id = Guid.NewGuid().ToString("N"); // user.userName = $"ypf{i}"; // user.userSex = $"男{i}"; // user.userAge = i; // user.addTime = DateTime.Now; // list.Add(user); // } // db.AddRange(list); // int count = db.SaveChanges(); //} #endregion #region 删除 //{ // var list = db.T_UserInfor.Where(u => true).Take(40000).ToList(); //需要事先插入相应的数据 // foreach (var user in list) // { // db.T_UserInfor.Remove(user); // } // int count = db.SaveChanges(); //} #endregion #region 修改 //{ // var list = db.T_UserInfor.Where(u => true).Take(40000).ToList(); //需要事先插入相应的数据 // foreach (var user in list) // { // int i = 0; // user.userName = $"ypf{i}"; // user.userSex = $"男{i}"; // user.userAge = i; // user.addTime = DateTime.Now; // i++; // } // int count = db.SaveChanges(); //} #endregion watch.Stop(); Console.WriteLine($"用时:{watch.ElapsedMilliseconds}"); } }
三. 常用组件支持
1. EFCore.BulkExtensions
(该组件已收费,不再更新使用)
(测试版本【3.3.1】,关于用法和之前的测试详见 https://www.cnblogs.com/yaopengfei/p/12205117.html)
(1). 功能测试
经测试EFCore5.0下, BulkInsert、BulkUpdate、BulkDelete(含条件删除)、整合事务均正常使用。BatchUpdate基于原值修改报错,直接改成新值可以使用。
代码分享:
using (EFDB01Context dbContext = new EFDB01Context()) { Stopwatch watch = new Stopwatch(); watch.Start(); //1. 增加 //{ // List<T_UserInfor> ulist1 = new List<T_UserInfor>(); // for (int i = 0; i < 100000; i++) // { // T_UserInfor userInfor = new T_UserInfor() // { // id = Guid.NewGuid().ToString("N"), // userName = $"ypf{i}", // userSex = $"男{i}", // userAge = i, // addTime = DateTime.Now // }; // ulist1.Add(userInfor); // } // dbContext.BulkInsert(ulist1); //} //2. 修改 //{ // List<T_UserInfor> ulist2 = new List<T_UserInfor>(); // for (int i = 0; i < 100; i++) // { // //此处不写的字段就会被更新成null了 // T_UserInfor userInfor = new T_UserInfor() // { // id = i.ToString(), // userName = "ypf1", // }; // ulist2.Add(userInfor); // } // dbContext.BulkUpdate(ulist2); //} //3. 删除 //{ // List<T_UserInfor> ulist3 = new List<T_UserInfor>(); // for (int i = 0; i < 100; i++) // { // T_UserInfor userInfor = new T_UserInfor() // { // id = i.ToString(), // }; // ulist3.Add(userInfor); // } // dbContext.BulkDelete(ulist3); //} //4.条件删除 //{ // int count1 = dbContext.T_UserInfor.Where(u => u.id.StartsWith("2")).BatchDelete(); //} //5.全表删除 //{ // dbContext.Truncate<T_UserInfor>(); //} //6. 条件更新 (未通过) //{ // //5.1 基于原有数据改 (error,报错) // int count2 = dbContext.T_UserInfor.Where(u => u.id.StartsWith("2")).BatchUpdate(a => new T_UserInfor() { userAge = a.userAge + 1 }); // //5.2 直接改成新数据 (ok) // //int count3 = dbContext.T_UserInfor.Where(u => u.id.StartsWith("2")).BatchUpdate(new T_UserInfor() { userSex = "女" }); //} //7. 事务 //{ // using (var transaction = dbContext.Database.BeginTransaction()) // { // try // { // List<T_UserInfor> ulist1 = new List<T_UserInfor>(); // //业务1 // for (int i = 0; i < 1; i++) // { // T_UserInfor userInfor = new T_UserInfor() // { // id = i.ToString(), // userName = "ypf", // userSex = "男", // userAge = 111, // addTime = DateTime.Now // }; // ulist1.Add(userInfor); // } // dbContext.BulkInsert(ulist1); // //业务2 // //int count2 = dbContext.T_UserInfor.Where(u => u.id.StartsWith("2")).BatchUpdate(a => new T_UserInfor() { id = a.id + "sdfsdfsdfsdf" }); //模拟错误 // T_UserInfor userInfor2 = new T_UserInfor() // { // id = Guid.NewGuid().ToString("N")+"fffff", //模拟错误 // userName = "ypf1", // userSex = "男1111", // userAge = 111, // addTime = DateTime.Now // }; // dbContext.Add(userInfor2); // dbContext.SaveChanges(); // //统一提交 // transaction.Commit(); // } // catch (Exception ex) // { // //using包裹不需要手写rollback // Console.WriteLine(ex.Message); // } // } //} watch.Stop(); Console.WriteLine($"用时:{watch.ElapsedMilliseconds}"); }
(2). 性能测试
(1). 增加
1000条:1.42s
1万条:1.4s
4万条:1.69s
10万条:2.6s
(2). 删除(测试的条件删除)
1000条:1.3s
1万条:1.38s
4万条:1.45s
(3). 修改 (测试的条件删除)
不单独测试,数量级相同
2. Z.EntityFramework.Plus.EFCore 【正常使用】
(测试版本【5.1.39】,关于用法和之前的测试详见 https://www.cnblogs.com/yaopengfei/p/14004719.html)
(1). 功能测试
经测试EFCore5.0下, Delete、Update、整合事务均正常使用。
代码分享:
{ using (EFDB01Context db = new EFDB01Context()) { Stopwatch watch = new Stopwatch(); watch.Start(); Console.WriteLine("开始执行。。。。。。"); #region 01-删除 //{ // int count1 = db.T_UserInfor.Where(u => u.id != "1").Delete(); // //db.T_UserInfor.Where(u => u.id != "1").Delete(x => x.BatchSize = 1000); //} #endregion #region 02-修改 //{ // //int count1 = db.T_UserInfor.Where(u => u.id != "1").Update(x => new T_UserInfor() { userSex = "男1234", userAge = 100 }); //} #endregion #region 03-测试事务 //{ // using (var transaction = db.Database.BeginTransaction()) // { // //using包裹,不需要手动写rollback // try // { // //1.普通增加 // for (int i = 0; i < 5; i++) // { // db.Add(new T_UserInfor() { id = i.ToString(), userName = "ypf"+i, addTime = DateTime.Now }); // } // db.SaveChanges(); // //2. 组件的删除 // db.T_UserInfor.Where(u => u.id == "1").Delete(); // //3. 组件的更新 // db.T_UserInfor.Where(u => u.id != "0001").Update(x => new T_UserInfor() { userAge = 10 }); // //4. 模拟失败(userName长度超了) // db.Add(new T_UserInfor() { id = Guid.NewGuid().ToString("N"), userName = Guid.NewGuid().ToString("N"), addTime = DateTime.Now }); // db.SaveChanges(); // //5.最后提交 // transaction.Commit(); // } // catch (Exception ex) // { // Console.WriteLine(ex.Message); // } // } //} #endregion Console.WriteLine("执行完成"); watch.Stop(); Console.WriteLine($"时间为:{watch.ElapsedMilliseconds}ms"); } }
(2).性能测试
更详细的性能测试比较可参考:https://www.cnblogs.com/yaopengfei/p/14441115.html
四. MySQL测试
(说明:Pomelo.EntityFrameworkCore.MySql 使用的是5.0.0正式版)
1.准备
(1)必备程序集
【Microsoft.EntityFrameworkCore】【Microsoft.EntityFrameworkCore.Design】【Microsoft.EntityFrameworkCore.Tools】【Pomelo.EntityFrameworkCore.MySql】
(2).映射指令
Scaffold-DbContext "Server=xxx;Database=EFDB01;User ID=root;Password=123456;" Pomelo.EntityFrameworkCore.MySql -OutputDir Entity -UseDatabaseNames -DataAnnotations -NoPluralize
2. 配置变化
(1). 直接在上下文OnConfiguring中写链接字符串,写法不变。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseMySql("server=xxxx;database=EFDB01;user id=root;password=xxxxx", Microsoft.EntityFrameworkCore.ServerVersion.Parse("5.7.28-mysql")); }
(2). 如果在ConfigureService中注入的写法发生了变化,多了1个参数:数据库版本
public void ConfigureServices(IServiceCollection services) {
services.AddDbContext<EFDB01Context>(option => option.UseMySql(Configuration.GetConnectionString("EFStr"), Microsoft.EntityFrameworkCore.ServerVersion.Parse("5.7.28-mysql")));
services.AddControllersWithViews();
}
3. 各种用法
(暂未测试。。。 可参照:https://www.cnblogs.com/yaopengfei/p/14004719.html)
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。