一. 新功能(變化)
前置
參考官方文檔: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 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。