asp.net core EFCore 屬性配置與DbContext
前言
Entity Framework (EF) Core 是輕量化、可擴(kuò)展、開源和跨平臺(tái)版的常用 Entity Framework 數(shù)據(jù)訪問技術(shù)。用于程序中的class類和數(shù)據(jù)庫(kù)中的表互相之間建立映射關(guān)系。在學(xué)習(xí)過程中,EFCore中的屬性配置顯的尤為重要它是學(xué)習(xí)好asp.net core的基礎(chǔ)是配置數(shù)據(jù)庫(kù)表結(jié)構(gòu)的重要基石。本篇內(nèi)容為學(xué)習(xí)與整理微軟文檔(MicrosoftDoc)筆者認(rèn)為重要部分具體內(nèi)容參見: Microsoft Build
EF Core 可用作對(duì)象關(guān)系映射程序 (O/RM),這可以實(shí)現(xiàn)以下兩點(diǎn):
- 使 .NET 開發(fā)人員能夠使用 .NET 對(duì)象處理數(shù)據(jù)庫(kù)。
- 無(wú)需再像通常那樣編寫大部分?jǐn)?shù)據(jù)訪問代碼。
EF Core 支持多個(gè)數(shù)據(jù)庫(kù)引擎,可通過名為數(shù)據(jù)庫(kù)提供程序的插件庫(kù)訪問許多不同的數(shù)據(jù)庫(kù)。
EF Core 提供程序通??绱我姹竟ぷ鳎豢缰饕姹竟ぷ?。 例如,針對(duì) EF Core 2.1 發(fā)布的提供程序應(yīng)用于 EF Core 2.2,但不適用于 EF Core 3.0。
使用 Entity Framework Core (EF Core) 時(shí)的典型工作單元包括:
1、創(chuàng)建 DbContext 實(shí)例
根據(jù)上下文跟蹤實(shí)體實(shí)例。 實(shí)體將在以下情況下被跟蹤
- 正在從查詢返回
- 正在添加或附加到上下文
- 根據(jù)需要對(duì)所跟蹤的實(shí)體進(jìn)行更改以實(shí)現(xiàn)業(yè)務(wù)規(guī)則
- 調(diào)用 SaveChanges 或 SaveChangesAsync。 EF Core 檢測(cè)所做的更改,并將這些更改寫入數(shù)據(jù)庫(kù)。
2、 釋放 DbContext 實(shí)例
3、重要
- 使用后釋放 DbContext 非常重要。 這可確保釋放所有非托管資源,并注銷任何事件或其他掛鉤,以防止在實(shí)例保持引用時(shí)出現(xiàn)內(nèi)存泄漏。
- DbContext 不是線程安全的。 不要在線程之間共享上下文。 請(qǐng)確保在繼續(xù)使用上下文實(shí)例之前,等待所有異步調(diào)用。 EF Core
- 代碼引發(fā)的 InvalidOperationException 可以使上下文進(jìn)入不可恢復(fù)的狀態(tài)。此類異常指示程序錯(cuò)誤,并且不旨在從其中恢復(fù)。
一、 ASP.NET Core 依賴關(guān)系注入中的 DbContext
可以使用 Startup.cs 的 ConfigureServices 方法中的 AddDbContext 將 EF Core 添加到此配置。 例如:
C#
。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContext<ApplicationDbContext>(
options => options.UseSqlServer("name=ConnectionStrings:DefaultConnection"));
}
此示例將名為 ApplicationDbContext 的 DbContext 子類注冊(cè)為 ASP.NET Core 應(yīng)用程序服務(wù)提供程序(也稱為依賴關(guān)系注入容器)中的作用域服務(wù)。 上下文配置為使用 SQL Server 數(shù)據(jù)庫(kù)提供程序,并將從 ASP.NET Core 配置讀取連接字符串。 在 ConfigureServices 中的何處調(diào)用 AddDbContext 通常不重要。
ApplicationDbContext 類必須公開具有 DbContextOptions << ApplicationDbContext> >參數(shù)的公共構(gòu)造函數(shù)。 這是將 AddDbContext 的上下文配置傳遞到 DbContext 的方式。 例如:C#
。
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
然后,ApplicationDbContext 可以通過構(gòu)造函數(shù)注入在 ASP.NET Core 控制器或其他服務(wù)中使用。 例如:c#
。
public class MyController
{
private readonly ApplicationDbContext _context;
public MyController(ApplicationDbContext context)
{
_context = context;
}
}
最終結(jié)果是為每個(gè)請(qǐng)求創(chuàng)建一個(gè) ApplicationDbContext 實(shí)例,并傳遞給控制器,以在請(qǐng)求結(jié)束后釋放前執(zhí)行工作單元。
二、DbContext 初始化與創(chuàng)建并配置模型
2.1、 DbContext 初始化
可以按照常規(guī)的 .NET 方式構(gòu)造 DbContext 實(shí)例,例如,使用 C# 中的 new。 可以通過重寫 OnConfiguring 方法或通過將選項(xiàng)傳遞給構(gòu)造函數(shù)來執(zhí)行配置。 例如:
- 重寫 OnConfiguring 方法
C#
。
public class ApplicationDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test");
}
}
通過此模式,還可以輕松地通過 DbContext 構(gòu)造函數(shù)傳遞配置(如連接字符串)。 例如:
- DbContext 構(gòu)造函數(shù)傳遞配置
C#
。
public class ApplicationDbContext : DbContext
{
private readonly string _connectionString;
public ApplicationDbContext(string connectionString)
{
_connectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString);
}
}
或者,可以使用 DbContextOptionsBuilder 創(chuàng)建 DbContextOptions 對(duì)象,然后將該對(duì)象傳遞到 DbContext 構(gòu)造函數(shù)。 這使得為依賴關(guān)系注入配置的 DbContext 也能顯式構(gòu)造。 例如,使用上述為 ASP.NET Core 的 Web 應(yīng)用定義的 ApplicationDbContext 時(shí):
ApplicationDbContext C#
。
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
2.2、創(chuàng)建并配置模型
Entity Framework Core 使用一組約定來根據(jù)實(shí)體類的形狀生成模型。 可指定其他配置以補(bǔ)充和/或替代約定的內(nèi)容。
模型配置主要分為兩種方法:
- FluentAPI
- 數(shù)據(jù)注解
2.2.1使用 fluent API 配置模型
可在派生上下文中替代 OnModelCreating 方法,并使用 ModelBuilder API 來配置模型。 此配置方法最為有效,并可在不修改實(shí)體類的情況下指定配置。 Fluent API 配置具有最高優(yōu)先級(jí),并將替代約定和數(shù)據(jù)注釋。
- Fluent API 配置
C#
。
using Microsoft.EntityFrameworkCore;
namespace EFModeling.EntityProperties.FluentAPI.Required;
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
#region Required
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.IsRequired();
}
#endregion
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
2.2.2分組配置
如果你有多個(gè)表需要配置。為了減小 OnModelCreating 方法的大小,可以將實(shí)體類型的所有配置提取到實(shí)現(xiàn) IEntityTypeConfiguration 的單獨(dú)類中。
- IEntityTypeConfiguration接口
C#
。
public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
builder
.Property(b => b.Url)
.IsRequired();
}
}
然后,只需從 OnModelCreating 調(diào)用 Configure 方法。
- OnModelCreating 調(diào)用 Configure 方法
C#
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
new BlogEntityTypeConfiguration().Configure(modelBuilder.Entity<Blog>());
}
也可以在給定程序集中應(yīng)用實(shí)現(xiàn) IEntityTypeConfiguration 的類型中指定的所有配置。
- IEntityTypeConfiguration 的類型中指定的所有配置
C#
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
modelBuilder.ApplyConfigurationsFromAssembly(typeof(BlogEntityTypeConfiguration).Assembly);
}
應(yīng)用配置的順序是不確定的,因此僅當(dāng)順序不重要時(shí)才應(yīng)使用此方法。
2.2.3使用數(shù)據(jù)注釋來配置模型
也可將特性(稱為數(shù)據(jù)注釋)應(yīng)用于類和屬性。 數(shù)據(jù)注釋會(huì)替代約定,但會(huì)被 Fluent API 配置替代。
- 數(shù)據(jù)注釋來配置模型
C#
。
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace EFModeling.EntityProperties.DataAnnotations.Annotations;
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
[Table("Blogs")]
public class Blog
{
public int BlogId { get; set; }
[Required]
public string Url { get; set; }
}
三、實(shí)體類型
在上下文中包含一種類型的 DbSet 意味著它包含在 EF Core 的模型中;我們通常將此類類型稱為實(shí)體。 EF Core 可以從/向數(shù)據(jù)庫(kù)中讀取和寫入實(shí)體實(shí)例,如果使用的是關(guān)系數(shù)據(jù)庫(kù),EF Core 可以通過遷移為實(shí)體創(chuàng)建表。
按照約定,上下文的 DbSet 屬性中公開的類型作為實(shí)體包含在模型中。 還包括在 OnModelCreating 方法中指定的實(shí)體類型,以及通過遞歸探索其他發(fā)現(xiàn)的實(shí)體類型的導(dǎo)航屬性找到的任何類型。
下面的代碼示例中包含了所有類型:
- 包含 Blog,因?yàn)樗谏舷挛牡?DbSet 屬性中公開。
- 包含 Post,因?yàn)樗峭ㄟ^ Blog.Posts 導(dǎo)航屬性發(fā)現(xiàn)的。
- 包含 AuditEntry因?yàn)樗?OnModelCreating 中指定的。
Fluent APIC#
。
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AuditEntry>();
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public Blog Blog { get; set; }
}
public class AuditEntry
{
public int AuditEntryId { get; set; }
public string Username { get; set; }
public string Action { get; set; }
}
四、實(shí)體屬性
模型中的每個(gè)實(shí)體類型都有一組屬性,EF Core 將從數(shù)據(jù)庫(kù)中讀取和寫入這些屬性。 如果使用的是關(guān)系數(shù)據(jù)庫(kù),實(shí)體屬性將映射到表列。
4.1、列名
按照約定,使用關(guān)系數(shù)據(jù)庫(kù)時(shí),實(shí)體屬性將映射到與屬性同名的表列。如果希望配置具有不同名稱的列,可以按以下代碼片段進(jìn)行操作:
Fluent API C#
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Id)
.HasColumnName("id");//數(shù)據(jù)庫(kù)表字段為英文小寫
}
4.2、列數(shù)據(jù)類型
使用關(guān)系數(shù)據(jù)庫(kù)時(shí),數(shù)據(jù)庫(kù)提供程序會(huì)根據(jù)屬性的 .NET 類型選擇數(shù)據(jù)類型。 它還會(huì)考慮其他元數(shù)據(jù),例如配置的最大長(zhǎng)度、屬性是否是主鍵的一部分等等。
例如,SQL Server 將 DateTime 屬性映射到 datetime2(7) 列,將 string 屬性映射到 nvarchar(max) 列(或?qū)τ谟米麈I的屬性,映射到 nvarchar(450))。
還可以配置列以指定列的確切數(shù)據(jù)類型。 例如,以下代碼將 Url 配置為非 unicode 字符串,其最大長(zhǎng)度為 200,并將 Rating 配置為十進(jìn)制,其精度為 5,小數(shù)位數(shù)為 2:
Fluent API C#
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>(
eb =>
{
eb.Property(b => b.Url).HasColumnType("varchar(200)");
eb.Property(b => b.Rating).HasColumnType("decimal(5, 2)");
});
}
4.3、最大長(zhǎng)度
配置最大長(zhǎng)度可向數(shù)據(jù)庫(kù)提供程序提供有關(guān)為給定屬性選擇適當(dāng)列數(shù)據(jù)類型的提示。 最大長(zhǎng)度僅適用于數(shù)組數(shù)據(jù)類型,如 string 和 byte[]。
在下面的示例中,將最大長(zhǎng)度配置為 500 將導(dǎo)致在 SQL Server 上創(chuàng)建 nvarchar(500) 類型的列:
下面展示一些 內(nèi)聯(lián)代碼片
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.HasMaxLength(500);
}
4.3、精度和小數(shù)位數(shù)
某些關(guān)系數(shù)據(jù)類型支持精度和小數(shù)位數(shù) Facet,它們用于控制可以存儲(chǔ)哪些值,以及列需要多少存儲(chǔ)。 哪些數(shù)據(jù)類型支持精度和小數(shù)位數(shù)取決于數(shù)據(jù)庫(kù),但在大多數(shù)數(shù)據(jù)庫(kù)中,decimal 和 DateTime 類型支持這些 Facet。 對(duì)于 decimal 屬性,精度用于定義表示列將包含的任何值所需的最大位數(shù),小數(shù)位數(shù)用于定義所需的最大小數(shù)位數(shù)。 對(duì)于 DateTime 屬性,精度用于定義表示秒的小數(shù)部分所需的最大位數(shù),不使用小數(shù)位數(shù)。
在以下示例中,將 Score 屬性配置為精度為 14 和小數(shù)位數(shù)為 2 將導(dǎo)致在 SQL Server 上創(chuàng)建 decimal(14,2) 類型的列,將 LastUpdated 屬性配置為精度為 3 將導(dǎo)致創(chuàng)建 datetime2(3) 類型的列:C#
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Score)
.HasPrecision(14, 2);
modelBuilder.Entity<Blog>()
.Property(b => b.LastUpdated)
.HasPrecision(3);
}
EF Core 5.0 中引入了用于配置精度和小數(shù)位數(shù)的 Fluent API。如果不先定義精度,則永遠(yuǎn)不會(huì)定義小數(shù)位數(shù),因此用于定義小數(shù)位數(shù)的 Fluent API 為 HasPrecision(precision, scale)。
4.4、Unicode
在某些關(guān)系數(shù)據(jù)庫(kù)中,存在不同的類型來表示 Unicode 和非 Unicode 文本數(shù)據(jù)。 例如,在 SQL Server 中,nvarchar(x)用于表示 UTF-16 中的 Unicode 數(shù)據(jù),而varchar(x)用于表示非 Unicode 數(shù)據(jù) (,但請(qǐng)參閱有關(guān) UTF-8 支持) SQL Server說明。 對(duì)于不支持此概念的數(shù)據(jù)庫(kù),配置此概念將不起作用。
默認(rèn)情況下,文本屬性配置為 Unicode。 可以將列配置為非 Unicode,如下所示:C#
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Book>()
.Property(b => b.Isbn)
.IsUnicode(false);
}
4.4必需和可選屬性
如果屬性包含 null 是有效的,則該屬性被視為可選屬性。 如果 null 不是要分配給屬性的有效值,則它被視為必需屬性。映射到關(guān)系數(shù)據(jù)庫(kù)架構(gòu)時(shí),必需屬性創(chuàng)建為不可為 null 的列,可選屬性創(chuàng)建為可為 null 的列。
按照約定,其 .NET 類型可包含 null 的屬性將配置為可選屬性,而 .NET 類型不能包含 null 的屬性將配置為必需屬性。例如,將具有 .NET 值類型的所有屬性 (int decimal bool、) 等等配置為必需,并且所有具有可為 null 的 .NET值類型 (int?decimal?等bool?屬性都配置為可選。
C# 8 引入了一項(xiàng)名為可為 null 引用類型 (NRT) 的新功能,該功能允許對(duì)引用類型進(jìn)行批注,指示引用類型能否包含 null。此功能默認(rèn)處于禁用狀態(tài),它會(huì)通過以下方式影響 EF Core的行為:
- 如果禁用了可為 null 的引用類型(默認(rèn)設(shè)置),則具有 .NET 引用類型的所有屬性都按約定配置為可選屬性(例如string)。
- 如果啟用了可為 null 的引用類型,則基于屬性的 .NET 類型的 C# 為 Null 性來配置屬性:string? 將配置為可選屬性,但 string 將配置為必需屬性。
- 以下示例演示一個(gè)具有必需和可選屬性的實(shí)體類型,分別說明了可為 null的引用功能在禁用(默認(rèn)設(shè)置)時(shí)和啟用時(shí)的情況:
禁用了可為 null 的引用類型C#(默認(rèn)設(shè)置)
。
public class CustomerWithoutNullableReferenceTypes
{
public int Id { get; set; }
[Required] // 根據(jù)需要配置所需的數(shù)據(jù)注釋
public string FirstName { get; set; }
[Required]
public string LastName { get; set; } // 根據(jù)需要配置所需的數(shù)據(jù)注釋
public string MiddleName { get; set; } // 可選的按照慣例
}
啟用了可為 null 的引用類型 C#可為 null 引用類型 (NRT)
。
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; } // Required by convention
public string LastName { get; set; } // Required by convention
public string? MiddleName { get; set; } // Optional by convention
//注意以下構(gòu)造函數(shù)綁定的使用,它避免了對(duì)未初始化的非空屬性的編譯警告。
public Customer(string firstName, string lastName, string? middleName = null)
{
FirstName = firstName;
LastName = lastName;
MiddleName = middleName;
}
}
五、OnModelCreating配置
5.1、顯式配置
按約定為可選屬性的屬性可以配置為必需屬性,如下所示:
C#
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.IsRequired();
}
5.2、默認(rèn)值
在關(guān)系數(shù)據(jù)庫(kù)中,可以為列配置默認(rèn)值;如果插入的行沒有該列的值,則將使用默認(rèn)值。
可以在屬性上配置默認(rèn)值: C"
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Rating)
.HasDefaultValue(3);
}
還可以指定用于計(jì)算默認(rèn)值的 SQL 片段: C#
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Created)
.HasDefaultValueSql("getdate()");
}
5.3、計(jì)算列
在大多數(shù)關(guān)系數(shù)據(jù)庫(kù)中,可以將列配置為在數(shù)據(jù)庫(kù)中計(jì)算其值,并且通常使用引用其他列的表達(dá)式:C#
。
modelBuilder.Entity<Person>()
.Property(p => p.DisplayName)
.HasComputedColumnSql("[LastName] + ', ' + [FirstName]");
以上命令將創(chuàng)建一個(gè)虛擬計(jì)算列,每次從數(shù)據(jù)庫(kù)中提取時(shí)都會(huì)計(jì)算其值。 你也可以將計(jì)算列指定為存儲(chǔ)(有時(shí)稱為持久化)計(jì)算列,這意味著系統(tǒng)會(huì)在每次更新行時(shí)計(jì)算該列,并將其與常規(guī)列一起存儲(chǔ)在磁盤上:
- EF Core 5.0 中添加了對(duì)創(chuàng)建存儲(chǔ)計(jì)算列的支持。
C#
。
modelBuilder.Entity<Person>()
.Property(p => p.NameLength)
.HasComputedColumnSql("LEN([LastName]) + LEN([FirstName])", stored: true);
5.4、主鍵
- 按照約定,如果應(yīng)用程序未提供值,則將類型為 short、int、long 或 Guid 的非復(fù)合主鍵設(shè)置為針對(duì)插入的實(shí)體生成值。
數(shù)據(jù)庫(kù)提供程序通常負(fù)責(zé)必要的配置;例如,SQL Server 中的數(shù)字主鍵會(huì)自動(dòng)設(shè)置為 IDENTITY 列。 - 根據(jù)約定,名為 Id 或 Id 的屬性將被配置為實(shí)體的主鍵。
可將單個(gè)屬性配置為實(shí)體的主鍵,如下所示:
下面展示一些C#
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Car>()
.HasKey(c => c.LicensePlate);
}
還可將多個(gè)屬性配置為實(shí)體的鍵 - 這稱為組合鍵。 組合鍵只能使用 Fluent API 進(jìn)行配置;約定永遠(yuǎn)不會(huì)設(shè)置組合鍵,并且不能使用數(shù)據(jù)注釋來配置組合鍵。
下面展示一些 內(nèi)聯(lián)代碼片
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Car>()
.HasKey(c => new { c.State, c.LicensePlate });
}
5.5、主鍵名稱
根據(jù)約定,在關(guān)系數(shù)據(jù)庫(kù)上,主鍵使用名稱 PK_ 進(jìn)行創(chuàng)建。 可按如下方式配置主鍵約束的名稱:
主鍵名稱 "HasName"C#
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasKey(b => b.BlogId)
.HasName("PrimaryKey_BlogId");
}
-
雖然 EF Core 支持使用任何基元類型的屬性作為主鍵(包括 string、Guid、byte[]
等),但并非所有數(shù)據(jù)庫(kù)都支持所有類型作為鍵。 在某些情況下,鍵值可以自動(dòng)轉(zhuǎn)換為支持的類型,否則應(yīng)手動(dòng)指定轉(zhuǎn)換。 -
向上下文添加新實(shí)體時(shí),鍵屬性必須始終具有非默認(rèn)值,但某些類型將由數(shù)據(jù)庫(kù)生成。 在這種情況下,當(dāng)添加實(shí)體以用于跟蹤時(shí),EF將嘗試生成一個(gè)臨時(shí)值。 調(diào)用 SaveChanges 后,臨時(shí)值將替換為數(shù)據(jù)庫(kù)生成的值。
-
如果鍵屬性的值由數(shù)據(jù)庫(kù)生成,并且在添加實(shí)體時(shí)指定了非默認(rèn)值,則 EF 將假定該實(shí)體已存在于數(shù)據(jù)庫(kù)中,并嘗試更新它,而不是插入新的實(shí)體。
若要避免這種情況,請(qǐng)禁用值生成。
六、值生成
6.1、 顯式配置值生成
EF Core 會(huì)自動(dòng)為主鍵設(shè)置值生成 - 但我們可能希望對(duì)非鍵屬性執(zhí)行相同的操作。 你可以將任何屬性配置為針對(duì)插入的實(shí)體生成其值,如下所示:
- 使用ValueGeneratedOnAdd
C#
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Inserted)
.ValueGeneratedOnAdd();
}
同樣,可以將屬性配置為在添加或更新時(shí)生成其值:
- 使用ValueGeneratedOnAddOrUpdate
C#
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.LastUpdated)
.ValueGeneratedOnAddOrUpdate();
}
警告
- 與默認(rèn)值或計(jì)算列不同,我們沒有指定值的生成方式;這取決于所使用的數(shù)據(jù)庫(kù)提供程序。
數(shù)據(jù)庫(kù)提供程序可能會(huì)自動(dòng)為某些屬性類型設(shè)置值生成,但其他屬性類型可能需要你手動(dòng)設(shè)置值的生成方式。 - 例如,在 SQL Server 上,如果 GUID 屬性配置為在添加時(shí)生成值,提供程序會(huì)自動(dòng)在客戶端執(zhí)行值生成,并使用算法生成最佳順序GUID 值。 但是,在 ValueGeneratedOnAdd DateTime 屬性上指定 將不起作用 。
- 同樣,配置為在添加或更新時(shí)生成值并標(biāo)記為并發(fā)標(biāo)記的 byte[] 屬性將設(shè)置為 rowversion 數(shù)據(jù)類型,以便在數(shù)據(jù)庫(kù)中自動(dòng)生成值。但是,指定 ValueGeneratedOnAdd 不起作用。
- 根據(jù)所使用的數(shù)據(jù)庫(kù)提供程序,值可能由 EF 在客戶端生成或在數(shù)據(jù)庫(kù)中生成。 如果值是由數(shù)據(jù)庫(kù)生成的,那么 EF可能會(huì)在你將實(shí)體添加到上下文時(shí)分配一個(gè)臨時(shí)值;在 SaveChanges() 期間,此臨時(shí)值將替換為數(shù)據(jù)庫(kù)生成的值。
6.2、日期/時(shí)間值生成
一個(gè)常見的請(qǐng)求是讓數(shù)據(jù)庫(kù)列包含第一次插入列的日期/時(shí)間(添加時(shí)生成的值)或上次更新列的日期/時(shí)間(添加或更新時(shí)生成的值)。 由于可通過各種策略執(zhí)行此操作,因此 EF Core 提供程序通常不會(huì)為日期/時(shí)間列自動(dòng)設(shè)置值生成 - 你必須自行配置。
6.2.1、創(chuàng)建時(shí)間戳
若要將日期/時(shí)間列配置為包含行的創(chuàng)建時(shí)間戳,通常需要使用適當(dāng)?shù)?SQL 函數(shù)來配置默認(rèn)值。 例如,在 SQL Server 上,你可以使用以下命令:
創(chuàng)建時(shí)間戳 C#
。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Created)
.HasDefaultValueSql("getdate()");
}
請(qǐng)確保選擇適當(dāng)?shù)暮瘮?shù),因?yàn)榭赡艽嬖诙鄠€(gè)函數(shù)(例如 GETDATE() 與 GETUTCDATE())。
6.2.1、更新時(shí)間戳
盡管存儲(chǔ)計(jì)算列看起來非常適合管理上次更新時(shí)間戳,但數(shù)據(jù)庫(kù)通常不允許在計(jì)算列中指定諸如 GETDATE() 之類的函數(shù)。 作為替代方法,你可以設(shè)置一個(gè)數(shù)據(jù)庫(kù)觸發(fā)器來達(dá)到同樣的效果:
更新時(shí)間戳 C#
。
CREATE TRIGGER [dbo].[Blogs_UPDATE] ON [dbo].[Blogs]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;
DECLARE @Id INT
SELECT @Id = INSERTED.BlogId
FROM INSERTED
UPDATE dbo.Blogs
SET LastUpdated = GETDATE()
WHERE BlogId = @Id
END
6.3、替代值生成
盡管為屬性配置了值生成,但在許多情況下,你仍然可以為其顯式指定一個(gè)值。 此操作能否真正起作用取決于已配置的特定值生成機(jī)制;雖然你可以指定顯式值而不是使用列的默認(rèn)值,但不能對(duì)計(jì)算列執(zhí)行相同的操作。
若要使用顯式值替代值生成,只需將屬性設(shè)置為該屬性類型的 CLR 默認(rèn)值(string 為 null,int 為 0,Guid 為 Guid.Empty,等等)以外的任意值。
6.3.1、將顯式值插入 IDENTITY 列中
默認(rèn)情況下,嘗試將顯式值插入 SQL Server IDENTITY 會(huì)失敗,因?yàn)镾QL Server 不允許將顯式值插入 IDENTITY 列中。 為此,必須在調(diào)用 SaveChanges() 之前手動(dòng)啟用 IDENTITY_INSERT,如下所示:文章來源:http://www.zghlxwxcb.cn/news/detail-466494.html
啟用 IDENTITY_INSERT C#
。文章來源地址http://www.zghlxwxcb.cn/news/detail-466494.html
using (var context = new ExplicitIdentityValuesContext())
{
context.Blogs.Add(new Blog { BlogId = 100, Url = "http://blog1.somesite.com" });
context.Blogs.Add(new Blog { BlogId = 101, Url = "http://blog2.somesite.com" });
context.Database.OpenConnection();
try
{
//啟用 IDENTITY_INSERT 開
context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT dbo.Blogs ON");
context.SaveChanges();
//啟用 IDENTITY_INSERT 關(guān)
context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT dbo.Blogs OFF");
}
finally
{
context.Database.CloseConnection();
}
}
到了這里,關(guān)于asp.net core EFCore 屬性配置與DbContext的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!