微軟官方在EFCore2.x開始,推薦使用DbContextPool以提高應用的性能。
Azure上使用的是SQL Server Basic Edition
Azure SQL的使用限制文檔:
一句話:付費級別和計算資源大小決定了Azure SQL最大會話數/請求數。
若要緩解,要么升級硬件資源,要么優化查詢利用率。
本次使用EFCore操作SQL Server的方式, 是官方默認用法:
依賴注入框架注冊一個自定義的 DbContext類型
在Controller構造函數中獲取 DbContext實例
這意味着每次請求都會創建一個 DbContext實例, 可以想象到
① 在高並發請求下,連接數不斷累積,最終某時刻會超過 Azure 的連接限制數量。
② 頻繁創建和銷毀 DbContext 實例,影響App Service自身性能。
EFCore2.0 為DbContext引入新的注冊方式:透明地注冊了 DbContext實例池:
services.AddDbContextPool<CarModelContext>(options => options.UseSqlServer(Configuration.GetConnectionString("SQL")));
- 一如既往支持lambda方式注冊連接字符串
- 默認的連接池數量為 128
- 每次使用完DbContext不會釋放對象,而是重置並回收到DBContextPool
Web程序中通過重用池中DbContext實例可提高高並發場景下的吞吐量, 這在概念上類似於ADO.NET Provider原生的連接池操作方式,具有節省DbContext實例化成本的優點, 這也是EFCore2.0 其中一個性能亮點。
高性能-DbContext 池
在 ASP.NET Core 應用程序中使用 EF Core 的基本模式通常包括將自定義 DbContext 類型注冊到依賴注入系統中,然后通過控制器中的構造函數參數獲取該類型的實例。這意味着為每個請求創建一個新的 DbContext 實例。
在 2.0 版本中,我們引入了一種在依賴注入中注冊自定義 DbContext 類型的新方法,它透明地引入了可重用的 DbContext 實例池。要使用 DbContext 池,請在服務注冊期間使用AddDbContextPool而不是:AddDbContext
services.AddDbContextPool<BloggingContext>(options => options.UseSqlServer(connectionString));
如果使用此方法,則在控制器請求 DbContext 實例時,我們將首先檢查池中是否有可用的實例。一旦請求處理完成,實例上的任何狀態都會被重置,並且實例本身會返回到池中。
這在概念上類似於連接池在 ADO.NET 提供程序中的操作方式,並且具有節省一些 DbContext 實例初始化成本的優勢。
高性能-限制
OnConfiguring()新方法在 DbContext的方法中引入了一些限制。
如果您在派生的 DbContext 類中維護自己的狀態(例如,私有字段),不應在請求之間共享,請避免使用 DbContext 池。EF Core 只會在將 DbContext 實例添加到池之前重置它所知道的狀態。
顯式編譯的查詢
這是第二個選擇加入的性能功能,旨在為大規模場景提供優勢。
手動或顯式編譯的查詢 API 在早期版本的 EF 和 LINQ to SQL 中都可用,以允許應用程序緩存查詢的轉換,以便它們可以只計算一次並執行多次。
雖然通常 EF Core 可以根據查詢表達式的散列表示自動編譯和緩存查詢,但這種機制可以通過繞過散列計算和緩存查找來獲得小的性能提升,允許應用程序使用已經通過調用委托編譯查詢。
// Create an explicitly compiled query
private static Func<CustomerContext, int, Customer> _customerById =
EF.CompileQuery((CustomerContext db, int id) =>
db.Customers
.Include(c => c.Address)
.Single(c => c.Id == id));
// Use the compiled query by invoking it
using (var db = new CustomerContext())
{
var customer = _customerById(db, 147);
}
更改跟蹤
tach 可以跟蹤新實體和現有實體的圖表
EF Core 支持通過多種機制自動生成鍵值。使用此功能時,如果鍵屬性是 CLR 默認值(通常為零或空),則會生成一個值。這意味着可以將實體圖傳遞給DbContext.AttachorDbSet.Attach
並且 EF Core
會將那些已設置鍵的實體標記為,Unchanged而那些沒有鍵集的實體將標記為Added。這使得在使用生成的密鑰時附加混合的新實體和現有實體的圖表變得容易。DbContext.Update
並DbSet.Update
以相同的方式工作,除了帶有鍵集的實體被標記為Modified
而不是Unchanged
.
改進的 LINQ 翻譯
使更多查詢能夠成功執行,在數據庫中評估更多邏輯(而不是在內存中),並且從數據庫中不必要地檢索更少的數據。
GroupJoin 改進
這項工作改進了為組連接生成的 SQL。組連接通常是對可選導航屬性的子查詢的結果
FromSql 和 ExecuteSqlCommand 中的字符串插值
C# 6 引入了字符串插值,該功能允許將 C# 表達式直接嵌入到字符串文字中,從而提供了一種在運行時構建字符串的好方法。在 EF Core 2.0 中,我們為接受原始 SQL 字符串的兩個主要 API 添加了對插值字符串的特殊支持:FromSql和ExecuteSqlCommand. 這種新的支持允許以“安全”的方式使用 C# 字符串插值。也就是說,以一種防止在運行時動態構造 SQL 時可能發生的常見 SQL 注入錯誤的方式。
var city = "London";
var contactTitle = "Sales Representative";
using (var context = CreateContext())
{
context.Set<Customer>()
.FromSql($@"
SELECT *
FROM ""Customers""
WHERE ""City"" = {city} AND
""ContactTitle"" = {contactTitle}")
.ToArray();
}
在此示例中,SQL 格式字符串中嵌入了兩個變量。EF Core 將生成以下 SQL:
@p0='London' (Size = 4000)
@p1='Sales Representative' (Size = 4000)
SELECT *
FROM ""Customers""
WHERE ""City"" = @p0
AND ""ContactTitle"" = @p1
EF.Functions.Like()
我們添加了 EF.Functions 屬性,EF Core 或提供程序可以使用它來定義映射到數據庫函數或運算符的方法,以便可以在 LINQ 查詢中調用這些方法。這種方法的第一個例子是 Like():
var aCustomers =
from c in context.Customers
where EF.Functions.Like(c.Name, "a%")
select c;
Like() 帶有一個內存實現,這在處理內存數據庫或需要在客戶端評估謂詞時非常方便。
參考:
https://stackoverflow.com/questions/48443567/adddbcontext-or-adddbcontextpool
https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.0#dbcontext-pooling