[譯] EF 6 新特性 - 中
阿新 • • 發佈:2022-01-19
介紹
接下來我將給大家重點介紹一下.Net 6 之後的一些新的變更,文章都是來自於外國大佬的文章,我這邊進行一個翻譯,並加上一些自己的理解和解釋。
源作者連結:https://blog.okyrylchuk.dev/entity-framework-core-6-features-part-1#heading-1-unicode-attribute
正文
列屬性
在模型中使用繼承時,您可能對建立的表中的預設 EF Core 列順序不滿意。在 EF Core 6.0 中,您可以使用 ColumnAttribute 指定列順序。
此外,您可以使用新的 Fluent API - HasColumnOrder()來實現。
public class EntityBase { [Column(Order = 1)] public int Id { get; set; } [Column(Order = 99)] public DateTime UpdatedOn { get; set; } [Column(Order = 98)] public DateTime CreatedOn { get; set; } } public class Person : EntityBase { [Column(Order = 2)] public string FirstName { get; set; } [Column(Order = 3)] public string LastName { get; set; } public ContactInfo ContactInfo { get; set; } } public class Employee : Person { [Column(Order = 4)] public string Position { get; set; } [Column(Order = 5)] public string Department { get; set; } } [Owned] public class ContactInfo { [Column(Order = 10)] public string Email { get; set; } [Column(Order = 11)] public string Phone { get; set; } } 遷移: protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "Employees", columns: table => new { Id = table.Column<int>(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), FirstName = table.Column<string>(type: "nvarchar(max)", nullable: true), LastName = table.Column<string>(type: "nvarchar(max)", nullable: true), Position = table.Column<string>(type: "nvarchar(max)", nullable: true), Department = table.Column<string>(type: "nvarchar(max)", nullable: true), ContactInfo_Email = table.Column<string>(type: "nvarchar(max)", nullable: true), ContactInfo_Phone = table.Column<string>(type: "nvarchar(max)", nullable: true), CreatedOn = table.Column<DateTime>(type: "datetime2", nullable: false), UpdatedOn = table.Column<DateTime>(type: "datetime2", nullable: false) }, constraints: table => { table.PrimaryKey("PK_Employees", x => x.Id); }); }
臨時表
EF Core 6.0 支援 SQL Server 時態表。可以將表配置為時間戳和歷史表的 SQL Server 預設值的臨時表。
public class ExampleContext : DbContext { public DbSet<Person> People { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder .Entity<Person>() .ToTable("People", b => b.IsTemporal()); } protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=TemporalTables;Trusted_Connection=True;"); } public class Person { public int Id { get; set; } public string Name { get; set; } } 遷移: protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "People", columns: table => new { Id = table.Column<int>(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Name = table.Column<string>(type: "nvarchar(max)", nullable: true), PeriodEnd = table.Column<DateTime>(type: "datetime2", nullable: false) .Annotation("SqlServer:IsTemporal", true) .Annotation("SqlServer:TemporalPeriodEndColumnName", "PeriodEnd") .Annotation("SqlServer:TemporalPeriodStartColumnName", "PeriodStart"), PeriodStart = table.Column<DateTime>(type: "datetime2", nullable: false) .Annotation("SqlServer:IsTemporal", true) .Annotation("SqlServer:TemporalPeriodEndColumnName", "PeriodEnd") .Annotation("SqlServer:TemporalPeriodStartColumnName", "PeriodStart") }, constraints: table => { table.PrimaryKey("PK_People", x => x.Id); }) .Annotation("SqlServer:IsTemporal", true) .Annotation("SqlServer:TemporalHistoryTableName", "PersonHistory") .Annotation("SqlServer:TemporalHistoryTableSchema", null) .Annotation("SqlServer:TemporalPeriodEndColumnName", "PeriodEnd") .Annotation("SqlServer:TemporalPeriodStartColumnName", "PeriodStart"); }
您可以使用以下方法查詢或檢索歷史資料:
- 時間AsOf
- 時間全部
- 時間從到
- 時間間隔
- 時間包含在
使用時態表:
using ExampleContext context = new();
context.People.Add(new() { Name = "Oleg" });
context.People.Add(new() { Name = "Steve" });
context.People.Add(new() { Name = "John" });
await context.SaveChangesAsync();
var people = await context.People.ToListAsync();
foreach (var person in people)
{
var personEntry = context.Entry(person);
var validFrom = personEntry.Property<DateTime>("PeriodStart").CurrentValue;
var validTo = personEntry.Property<DateTime>("PeriodEnd").CurrentValue;
Console.WriteLine($"Person {person.Name} valid from {validFrom} to {validTo}");
}
// Output:
// Person Oleg valid from 06-Nov-21 17:50:39 PM to 31-Dec-99 23:59:59 PM
// Person Steve valid from 06-Nov-21 17:50:39 PM to 31-Dec-99 23:59:59 PM
// Person John valid from 06-Nov-21 17:50:39 PM to 31-Dec-99 23:59:59 PM
查詢歷史資料:
var oleg = await context.People.FirstAsync(x => x.Name == "Oleg");
context.People.Remove(oleg);
await context.SaveChangesAsync();
var history = context
.People
.TemporalAll()
.Where(e => e.Name == "Oleg")
.OrderBy(e => EF.Property<DateTime>(e, "PeriodStart"))
.Select(
p => new
{
Person = p,
PeriodStart = EF.Property<DateTime>(p, "PeriodStart"),
PeriodEnd = EF.Property<DateTime>(p, "PeriodEnd")
})
.ToList();
foreach (var pointInTime in history)
{
Console.WriteLine(
$"Person {pointInTime.Person.Name} existed from {pointInTime.PeriodStart} to {pointInTime.PeriodEnd}");
}
// Output:
// Person Oleg existed from 06-Nov-21 17:50:39 PM to 06-Nov-21 18:11:29 PM
檢索歷史資料:
var removedOleg = await context
.People
.TemporalAsOf(history.First().PeriodStart)
.SingleAsync(e => e.Name == "Oleg");
Console.WriteLine($"Id = {removedOleg.Id}; Name = {removedOleg.Name}");
// Output:
// Id = 1; Name = Oleg
稀疏列
EF Core 6.0 支援 SQL Server 稀疏列。它在使用 TPH(每層次表)繼承對映時很有用。
public class ExampleContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<Employee> Employees { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<User>()
.Property(e => e.Login)
.IsSparse();
modelBuilder
.Entity<Employee>()
.Property(e => e.Position)
.IsSparse();
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=SparseColumns;Trusted_Connection=True;");
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public class User : Person
{
public string Login { get; set; }
}
public class Employee : Person
{
public string Position { get; set; }
}
遷移:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "People",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(max)", nullable: false),
Discriminator = table.Column<string>(type: "nvarchar(max)", nullable: false),
Position = table.Column<string>(type: "nvarchar(max)", nullable: true)
.Annotation("SqlServer:Sparse", true),
Login = table.Column<string>(type: "nvarchar(max)", nullable: true)
.Annotation("SqlServer:Sparse", true)
},
constraints: table =>
{
table.PrimaryKey("PK_People", x => x.Id);
});
}
結語
聯絡作者:加群:867095512 @MrChuJiu