資料庫數據行可以透過各種方式產生其值:主鍵數據行經常是自動遞增整數、其他數據行具有預設或計算值等等。此頁面詳細說明使用 EF Core 產生組態值的各種模式。
預設值
在關係資料庫上,可以使用預設值來設定數據行;如果插入數據列時沒有該數據行的值,則會使用預設值。
您可以在屬性上設定預設值:
modelBuilder.Entity<Blog>()
.Property(b => b.Rating)
.HasDefaultValue(3);
您也可以指定用來計算預設值的 SQL 片段:
modelBuilder.Entity<Blog>()
.Property(b => b.Created)
.HasDefaultValueSql("getdate()");
從 EF 10 開始,針對 SQL Server,您可以明確指定預設值條件約束的名稱,讓您更充分掌控資料庫架構。
modelBuilder.Entity<Blog>()
.Property(b => b.Rating)
.HasDefaultValue(3, "DF_Blog_IsActive");
modelBuilder.Entity<Blog>()
.Property(b => b.Created)
.HasDefaultValueSql("getdate()" , "DF_Blog_IsActive");
您也可以呼叫 UseNamedDefaultConstraints
來啟用所有預設條件約束的自動命名。 請注意,如果您有現有的移轉,則您新增的下一個移轉將會重新命名模型中的每一個默認條件約束。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.UseNamedDefaultConstraints();
}
計算資料行
在大部分關係資料庫上,數據行可以設定為在資料庫中計算其值,通常是參考其他數據行的表達式:
modelBuilder.Entity<Person>()
.Property(p => p.DisplayName)
.HasComputedColumnSql("[LastName] + ', ' + [FirstName]");
上述會建立 虛擬 計算數據行,其值會在每次從資料庫擷取時計算。 您也可以指定儲存計算資料 列(有時 稱為 持續性),這表示它會在資料列的每個更新上計算,並且會與一般數據行一起儲存在磁碟上:
modelBuilder.Entity<Person>()
.Property(p => p.NameLength)
.HasComputedColumnSql("LEN([LastName]) + LEN([FirstName])", stored: true);
主鍵
根據慣例,如果應用程式未提供值,則類型為 short、int、long 或 Guid 的非複合主鍵會設定為為插入實體產生值。 您的資料庫提供者通常會負責必要的設定;例如,SQL Server 中的數值主鍵會自動設定為 IDENTITY 資料行。
如需詳細資訊,請參閱有關密鑰的文件和特定繼承對應策略的指引。
明確配置值生成过程
我們上面看到,EF Core 會自動為主鍵設定值生成,但我們可能也希望對非鍵屬性進行相同的設定。 您可以設定任何屬性,使其針對插入的實體產生其值,如下所示:
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime Inserted { get; set; }
}
同樣地,屬性可以設定為在新增或更新時產生其值:
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime LastUpdated { get; set; }
}
不同於預設值或計算數據行,我們不會指定如何產生值;取決於所使用的資料庫提供者。 資料庫提供者可能會自動設定某些屬性類型的值產生,但其他提供者可能會要求您手動設定如何產生值。
例如,在 SQL Server 上,當 GUID 屬性設定為主鍵時,提供者會在用戶端自動使用演算法,生成最佳的循序 GUID 值。 不過,指定 ValueGeneratedOnAdd 在 DateTime 屬性上將不會有任何作用(請參閱下方章節了解 DateTime 值的生成)。
同樣地,設定為在新增或更新時產生的 byte[] 屬性,並標記為並行控制符的屬性,會以 rowversion 數據類型設置,以便在資料庫中自動產生值。 不過,指定 ValueGeneratedOnAdd 沒有任何作用。
請參閱提供者的文件,以了解其支援的具體值生成技術。 您可以 在這裡找到 SQL Server 值產生檔案。
產生日期/時間值
常見的要求是具有資料庫數據行,其中包含第一次插入數據列的日期/時間(新增時產生的值),或上次更新時(新增或更新時產生的值)。 由於有各種策略可以這麼做,EF Core 提供者通常不會針對日期/時間資料行自動設定值產生 - 您必須自行設定。
建立時間戳
將日期/時間數據行設定為具有數據列的建立時間戳,通常是使用適當的 SQL 函式設定預設值。 例如,在 SQL Server 上,您可以使用下列內容:
modelBuilder.Entity<Blog>()
.Property(b => b.Created)
.HasDefaultValueSql("getdate()");
請務必選取適當的函式,因為可能有數個函式存在(例如 GETDATE()
與 GETUTCDATE()
)。
更新時間戳
雖然預存計算數據行似乎是管理上次更新時間戳的好解決方案,但資料庫通常不允許在計算數據行中指定這類 GETDATE()
函式。 或者,您可以設定資料庫觸發程式以達到相同的效果:
CREATE TRIGGER [dbo].[Blogs_UPDATE] ON [dbo].[Blogs]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;
UPDATE B
SET LastUpdated = GETDATE()
FROM dbo.Blogs AS B
INNER JOIN INSERTED AS I
ON B.BlogId = I.BlogId
END
如需建立觸發程式的詳細資訊,請參閱在移轉中操作原始 SQL 的文件。
覆蓋值生成
雖然屬性已設定為產生值,但在許多情況下,您仍可能會明確指定其值。 這是否實際運作取決於已設定的特定值產生機制:雖然您可以指定明確的值,而不是使用數據行的預設值,但計算數據行無法執行相同的作業。
若要使用明確的值覆蓋值生成,只需將屬性設定為任何不是該屬性類型的 CLR 預設值的值(例如,null
對於 string
、0
對於 int
、Guid.Empty
對於 Guid
等)。
備註
嘗試將明確值插入 SQL Server IDENTITY 預設會失敗; 如需因應措施,請參閱這些檔。
若要為被設定為在新增或更新時會產生值的屬性提供明確的值,您也必須如下設定屬性:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().Property(b => b.LastUpdated)
.ValueGeneratedOnAddOrUpdate()
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Save);
}
無值產生
除了上述案例等特定案例之外,屬性通常未設定任何值產生;這表示應用程式必須一律提供要儲存至資料庫的值。 此值必須先指派給新實體,才能新增至內容。
不過,在某些情況下,您可能會想停用根據慣例設定的值生成。 例如,int 類型的主鍵通常會隱含地設定為新增時自動產生值(例如,在 SQL Server 中為身份欄位)。 您可以透過下列方式停用此專案:
public class Blog
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int BlogId { get; set; }
public string Url { get; set; }
}