閱讀須知:本文為入門介紹、指引文章,所示代碼皆為最簡易(或僅為實作功能)的演示示例版本,不一定切實符合個人(企業)實際開發需求,
一、DbContext生存期
DbContext 的生存期從創建實體時開始,并在釋放實體時結束, DbContext 實體旨在用于單個作業單元,這意味著 DbContext 實體的生存期通常很短,
使用 Entity Framework Core (EF Core) 時的典型作業單元包括:
- 創建 DbContext 實體
- 根據背景關系跟蹤物體實體, 物體將在以下情況下被跟蹤
- 正在從查詢回傳
- 正在添加或附加到背景關系
- 根據需要對所跟蹤的物體進行更改以實作業務規則
- 呼叫 SaveChanges 或 SaveChangesAsync, EF Core 檢測所做的更改,并將這些更改寫入資料庫
- 釋放 DbContext 實體
注意事項:
1、使用后釋放 DbContext 非常重要,這可確保釋放所有非托管資源,并注銷任何事件或其他掛鉤,以防止在實體保持參考時出現記憶體泄漏,
2、DbContext 不是執行緒安全的,不要在執行緒之間共享背景關系,請確保在繼續使用背景關系實體之前,等待所有異步呼叫,
3、EF Core 代碼引發的 InvalidOperationException 可以使背景關系進入不可恢復的狀態,此類例外指示程式錯誤,并且不旨在從其中恢復,
二、ASP.NET Core 依賴關系注入中的 DbContext
在許多 Web 應用程式中,每個 HTTP 請求都對應于單個作業單元,這使得背景關系生存期與請求的生存期相關,成為 Web 應用程式的一個良好默認值,
使用依賴關系注入配置 ASP.NET Core 應用程式,可以在 Startup.cs
檔案的 ConfigureServices
方法中,用 AddDbContext 擴展方法將 EF Core 添加到此處進行配置,
本文中我使用的是 MySQL 資料庫,例如:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
#region 配置MySQL資料庫
var connectionString = "server=資料庫部署的服務器地址;user=資料庫登錄用戶名;password=資料庫登錄密碼;database=資料庫名;charset=utf8";
var serverVersion = new MySqlServerVersion(new Version(5, 7, 22));
services.AddDbContext<CustomAppDbContext>(
dbContextOptions => dbContextOptions
.UseMySql(connectionString, serverVersion)
);
#endregion
}
此示例將名為 CustomAppDbContext 的 DbContext 子類注冊為 ASP.NET Core 應用程式服務提供程式(也稱為依賴關系注入容器)中的作用域服務,背景關系配置為使用 MySQL 資料庫提供程式,并將從 ASP.NET Core 配置讀取連接字串,在 ConfigureServices 中的何處呼叫 AddDbContext 通常不重要,
F12轉到 DbContext 類的定義,發現其有參建構式定義形式為:
public DbContext([NotNullAttribute] DbContextOptions options);
CustomAppDbContext 類必須公開具有 DbContextOptions<CustomAppDbContext> 引數的公共建構式,這是將 AddDbContext 的背景關系配置傳遞到 DbContext 的方式,例如:
public class CustomAppDbContext : DbContext
{
public CustomAppDbContext(DbContextOptions<CustomAppDbContext> options) : base(options)//呼叫父類的建構式
{
}
public DbSet<Student> Student { get; set; }
}
其中 Student 類如下所示:
public partial class Student
{
public string Id { get; set; }
public string Name { get; set; }
public DateTime? JoinTime { get; set; }
public int Sex { get; set; }
}
然后,CustomAppDbContext 可以通過建構式注入在 ASP.NET Core 控制器或其他服務中使用,例如:
public class MyController : Controller
{
private readonly CustomAppDbContext _context;
public MyController(CustomAppDbContext context)//建構式
{
_context = context;
}
public JsonResult Index()
{
_context.Student.Add(new Student
{
Id = "10001",
Name = "張三",
JoinTime = DateTime.Now,
Sex = 1
});
_context.SaveChanges();
return new JsonResult("success");
}
}
最終結果是為每個請求創建一個 CustomAppDbContext 實體,并傳遞給控制器,以在請求結束后釋放前執行作業單元,
三、使用“new”的簡單的 DbContext 初始化
可以按照常規的 .NET 方式構造 DbContext 實體,例如,使用 C# 中的 new
,可以通過重寫 OnConfiguring 方法或通過將選項傳遞給建構式來執行配置,
1、重寫 OnConfiguring 方法,
F12轉到 DbContext 類的定義,發現 OnConfiguring 方法的定義形式為:
protected internal virtual void OnConfiguring(DbContextOptionsBuilder optionsBuilder);
DbContext 子類的代碼示例如下所示:
public class NewCustomAppDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySql("server=資料庫部署的服務器地址;user=資料庫登錄用戶名;password=資料庫登錄密碼;database=資料庫名;charset=utf8", new MySqlServerVersion(new Version(5, 7, 22)));
}
public DbSet<Student> Student { get; set; }
}
此種方式構造的 DbContext 實體在控制器方法中呼叫如下所示:
public class MyNewController : Controller
{
public string Index()
{
using var db = new NewCustomAppDbContext();
var list = db.Student.ToList();
return JsonConvert.SerializeObject(list);
}
}
2、通過 DbContext 建構式傳遞配置
通過此模式,我們還可以輕松地通過 DbContext 建構式傳遞配置(如連接字串),例如:
public class NewCustomAppDbContext : DbContext
{
private readonly string _connectionString;
public NewCustomAppDbContext(string connectionString)
{
_connectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySql(_connectionString, new MySqlServerVersion(new Version(5, 7, 22)));
}
public DbSet<Student> Student { get; set; }
}
此種方式構造的 DbContext 實體在控制器方法中呼叫如下所示:
public class MyNewController : Controller
{
public string Index()
{
using var db = new NewCustomAppDbContext("server=資料庫部署的服務器地址;user=資料庫登錄用戶名;password=資料庫登錄密碼;database=資料庫名;charset=utf8");
var list = db.Student.ToList();
return JsonConvert.SerializeObject(list);
}
}
3、使用 DbContextOptionsBuilder 創建 DbContextOptions 物件
可以使用 DbContextOptionsBuilder 創建 DbContextOptions 物件,然后將該物件傳遞到 DbContext 建構式,這使得為依賴關系注入配置的 DbContext 也能顯式構造,例如:
public class DICustomAppDbContext:DbContext
{
public DICustomAppDbContext(DbContextOptions<DICustomAppDbContext> optionsBuilder):base(optionsBuilder)
{
}
public DbSet<Student> Student { get; set; }
}
此種構造方式,在 Controller 中可以創建 DbContextOptions,并可以顯式呼叫建構式,代碼如下所示:
public class MyDIController : Controller
{
private readonly string _connectionString = "server=資料庫部署的服務器地址;user=資料庫登錄用戶名;password=資料庫登錄密碼;database=資料庫名;charset=utf8";
private readonly MySqlServerVersion _serverVersion = new MySqlServerVersion(new Version(5, 7, 22));
public string Index()
{
var contextOptions = new DbContextOptionsBuilder<DICustomAppDbContext>()
.UseMySql(_connectionString, _serverVersion)
.Options;
using var context = new DICustomAppDbContext(contextOptions);
var list = context.Student.ToList();
return JsonConvert.SerializeObject(list);
}
}
四、使用 DbContext 工廠
某些應用程式型別(例如 ASP.NET Core Blazor)使用依賴關系注入,但不創建與所需的 DbContext 生存期一致的服務作用域,即使存在這樣的對齊方式,應用程式也可能需要在此作用域內執行多個作業單元,例如,單個 HTTP 請求中的多個作業單元,
在這些情況下,可以使用 AddDbContextFactory 來注冊工廠以創建 DbContext 實體,例如:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
#region 配置MySQL資料庫
var connectionString = "server=資料庫部署的服務器地址;user=資料庫登錄用戶名;password=資料庫登錄密碼;database=資料庫名;charset=utf8";
var serverVersion = new MySqlServerVersion(new Version(5, 7, 22));
services.AddDbContextFactory<FactoryCustomAppDbContext>(
dbContextOptions => dbContextOptions
.UseMySql(connectionString, serverVersion)
);
#endregion
}
FactoryCustomAppDbContext 類必須公開具有 DbContextOptions<FactoryCustomAppDbContext> 引數的公共建構式,此模式與上面傳統 ASP.NET Core 部分中使用的模式相同,例如:
public class FactoryCustomAppDbContext : DbContext
{
public FactoryCustomAppDbContext(DbContextOptions<FactoryCustomAppDbContext> options) : base(options)
{
}
public DbSet<Student> Student { get; set; }
}
然后,可以通過建構式注入在其他服務中使用 DbContextFactory 工廠,最后,可以使用注入的工廠在服務代碼中構造 DbContext 實體,例如:
public class MyFactoryController : Controller
{
private readonly IDbContextFactory<FactoryCustomAppDbContext> _contextFactory;
public MyFactoryController(IDbContextFactory<FactoryCustomAppDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
public string Index()
{
using (var context = _contextFactory.CreateDbContext())
{
var list = context.Student.ToList();
return JsonConvert.SerializeObject(list);
}
}
}
請注意,以這種方式創建的 DbContext 實體并非由應用程式的服務提供程式進行管理,因此必須由應用程式釋放,
五、DbContextOptions
所有 DbContext 配置的起始點都是 DbContextOptionsBuilder,可以通過以下三種方式獲取此生成器:
- 在 AddDbContext 和相關方法中
- 在 OnConfiguring 中
- 使用 new 顯式構造
每種配置方式的示例在本文上述內容中都進行了講解和代碼展示,無論生成器來自何處,都可以應用相同的配置,此外,無論如何構造背景關系,都將始終呼叫 OnConfiguring,這意味著即使使用 AddDbContext,OnConfiguring 也可用于執行其他配置,
六、配置資料庫提供程式
每個 DbContext 實體都必須配置為使用一個且僅一個資料庫提供程式,(DbContext 子型別的不同實體可用于不同的資料庫提供程式,但一個實體只能使用一個,)一個資料庫提供程式要使用一個特定的 Use*
呼叫進行配置,
例如,若要使用 MySQL 資料庫提供程式:
public class MySQLAppDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySql("資料庫連接字串", new MySqlServerVersion(new Version(5, 7, 22)));
}
}
例如,若要使用 SQL Server 資料庫提供程式:
public class SQLServerApplicationDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("資料庫連接字串");
}
}
這些 Use*
方法是由資料庫提供程式實作的擴展方法, 這意味著必須先安裝資料庫提供程式 NuGet 包,然后才能使用擴展方法,
EF Core 資料庫提供程式廣泛使用擴展方法,如果編譯器指示找不到方法,請確保已安裝提供程式的 NuGet 包,并且在代碼中已有 using Microsoft.EntityFrameworkCore;,
下表包含常見資料庫提供程式的示例,
資料庫系統 | 配置示例 | NuGet 程式包 |
---|---|---|
SQL Server 或 Azure SQL | .UseSqlServer(connectionString) | Microsoft.EntityFrameworkCore.SqlServer |
Azure Cosmos DB | .UseCosmos(connectionString, databaseName) | Microsoft.EntityFrameworkCore.Cosmos |
SQLite | .UseSqlite(connectionString) | Microsoft.EntityFrameworkCore.Sqlite |
EF Core 記憶體中資料庫 | .UseInMemoryDatabase(databaseName) | Microsoft.EntityFrameworkCore.InMemory |
PostgreSQL | .UseNpgsql(connectionString) | Npgsql.EntityFrameworkCore.PostgreSQL |
MySQL/MariaDB | .UseMySql(connectionString) | Pomelo.EntityFrameworkCore.MySql |
Oracle | .UseOracle(connectionString) | Oracle.EntityFrameworkCore |
六、避免 DbContext 執行緒處理問題
Entity Framework Core 不支持在同一 DbContext 實體上運行多個并行操作,這包括異步查詢的并行執行以及從多個執行緒進行的任何顯式并發使用,因此,始終立即 await
異步呼叫,或對并行執行的操作使用單獨的 DbContext 實體,
當 EF Core 檢測到嘗試同時使用 DbContext 實體的情況時,你將看到 InvalidOperationException
,其中包含類似于以下內容的訊息:
A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext, however instance members are not guaranteed to be thread safe.
翻譯成中文就是:
在上一個操作完成之前,在此背景關系上啟動了第二個操作,這通常是由不同執行緒使用相同的DbContext實體引起的,但不保證實體成員是執行緒安全的,
當并發訪問未被檢測到時,可能會導致未定義的行為、應用程式崩潰和資料損壞,
七、異步操作缺陷
使用異步方法,EF Core 可以啟動以非阻擋式訪問資料庫的操作,但是,如果呼叫方不等待其中一個方法完成,而是繼續對 DbContext 執行其他操作,則 DbContext 的狀態可能會(并且很可能會)損壞,
應始終立即等待 EF Core 異步方法,
-------------------------------本篇文章到此結束-------------------------------------
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/506470.html
下一篇:后增量的邏輯是什么?