在EF7中,創建一個模型是非常重要的步驟,本文將使用微軟官方檔案中的指南,來學習EF7中的創建模型篇,外加一點點個人理解,
物體型別
在 EF7 中,你需要使用 modelBuilder.Entity
如果你的資料庫中有多個模式(schema),你可以使用 ToTable() 方法的另一個多載版本來指定表所屬的架構,如果你想要為生成的表添加注釋,可以使用 HasComment() 方法,如果你不想將某個類映射到資料庫中的表,我們可以使用 modelBuilder.Entity
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.ToTable("Blogs", schema: "dbo") //.ToTable("Blogs");
.HasComment("This table contains blog posts.");
modelBuilder.Ignore<Address>();
}
共享型別物體型別
在 EF7 中,你可以將一個型別映射到多個表中,這種情況通常發生在你有一組具有相似屬性的型別,這些屬性在不同的表中都需要使用,在這種情況下,你可以使用 ModelBuilder.SharedTypeEntity() 方法來創建一個物體型別,并將其映射到多個表中,
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var addressEntity = modelBuilder.SharedTypeEntity<Address>("Address");
addressEntity.ToTable("CustomerBillingAddresses");
addressEntity.ToTable("CustomerShippingAddresses");
modelBuilder.Entity<Customer>()
.OwnsOne(c => c.BillingAddress, b =>
{
b.WithOwner().HasForeignKey("BillingAddressId");
b.ToTable("CustomerBillingAddresses");
});
modelBuilder.Entity<Customer>()
.OwnsOne(c => c.ShippingAddress, b =>
{
b.WithOwner().HasForeignKey("ShippingAddressId");
b.ToTable("CustomerShippingAddresses");
});
}
在上面的代碼中,我們首先使用 ModelBuilder.SharedTypeEntity() 方法創建一個名為 Address 的物體型別,然后,我們使用 ToTable() 方法將該物體型別映射到多個表中,接下來,我們使用 OwnsOne() 方法來將 BillingAddress 和 ShippingAddress 屬性映射到具有相應名稱的表中,注意,我們還使用了 HasForeignKey() 方法來指定外鍵的名稱,
使用共享型別物體型別可以使你的代碼更加簡潔,并提高可維護性,通過使用共享型別物體型別,你可以將一個型別映射到多個表中,而不必在每個物體型別中都定義相同的映射代碼,
物體屬性
如果要排除物體屬性,可以使用Ignore()方法,
modelBuilder.Entity<Person>().Ignore(p => p.Age)
定義列名,例如,下面的代碼將為Person類中的LastName屬性定義列名,
modelBuilder.Entity<Person>().Property(p => p.LastName).HasColumnName("Last_Name")
定義列注釋,例如,下面的代碼將為Person類中的LastName屬性定義注釋,
modelBuilder.Entity<Person>().Property(p => p.LastName).HasComment("The last name of the person")
定義列排序規則,例如,下面的代碼將為Person類中的LastName屬性定義排序規則,
modelBuilder.Entity<Person>().Property(p => p.LastName).UseCollation("SQL_Latin1_General_CP1_CI_AS");
定義列的資料型別(和資料庫一致即可),例如,下面的代碼將為Person類中的Age屬性定義為int型別:
modelBuilder.Entity<Person>().Property(p => p.Age).HasColumnType("int");
定義列的最大長度,例如,下面的代碼將為Person類中的FirstName屬性定義為50個字符的最大長度:
modelBuilder.Entity<Person>().Property(p => p.FirstName).HasMaxLength(50);
定義列的精度和小數位數,例如,下面的代碼將為Person類中的Height屬性定義為2位小數的精度:
modelBuilder.Entity<Person>().Property(p => p.Height).HasPrecision(5, 2);
定義是否為必需或可選屬性,例如,下面的代碼將為Person類中的FirstName屬性定義為必需屬性:
modelBuilder.Entity<Person>().Property(p => p.FirstName).IsRequired();
定義列在表中的順序,例如,下面的代碼將為Person類中的FirstName屬性定義為表中第二個列:
modelBuilder.Entity<Person>().Property(p => p.Id).HasColumnOrder(1);
modelBuilder.Entity<Person>().Property(p => p.FirstName).HasColumnOrder(2);
鍵
主鍵
定義主鍵,根據約定,名為 Id 或 <型別名稱>Id 的屬性將被配置為物體的主鍵,
internal class User
{
public string Id { get; set; } // 主鍵
public string Name { get; set; }
}
如果我們不想使用默認約定規則,可以自定義規則,下面的代碼將指定User類的Id屬性作為主鍵并且重寫設定主鍵的名稱:
modelBuilder.Entity<Person>().HasKey(p => p.Id).HasName("UserId");
注意主鍵Id應是有序的
在 MySQL 中,主鍵 Id 不是有順序的時候,可能會導致新增性能下降的原因是,MySQL 默認使用 B-tree 索引來實作主鍵索引,如果主鍵 Id 是無序的,那么在插入資料時,MySQL 需要不斷尋找合適的位置來插入新資料,這可能會導致 B-tree 索引不斷被調整,從而影響插入性能,相反,如果主鍵 Id 是有序的,MySQL 可以更快速地找到要插入的位置,從而提高插入性能,
注意主鍵Id應該是最后生成的
有些程式可能會有延遲,導致資料庫插入是非有序的,場景:假如我們先生成Id再處理業務邏輯,有兩個執行緒,同時并發請求,第一個執行緒生成好Id 是 3,處理下面業務邏輯時發生了大概五毫秒的延遲,第二個執行緒也生成好了Id是 4,處理下面業務邏輯時沒有延遲,就通過了,所以第二個執行緒,先進資料庫插入Id為4,第一個執行緒因為有延遲來慢了一步,插入的Id是3,資料庫Id就變成無序的了,
使用基于時間戳的有序Guid作為主鍵
對于非復合數字和 GUID 主鍵,EF Core 根據約定設定值生成,
EF7中 Guid 是基于時間的演算法精確到納秒,因為一毫秒等于一百萬納秒,所以EF7的Guid一毫秒可以產生一百萬的Id,
優點:不可預測、有序(添加性能高)、在支持Guid(uuid)的資料庫中(查詢性能高)、開箱即用,
缺點:(這是可以忽略不計的事情)并發中在同一納秒內,產生的Id是會重復的,有時鐘回撥問題,太長,
EF7中的Guid有序演算法比雪花演算法更好,
- 雪花演算法在并發時,也會重復,因為序列號和時間戳,即使我們配置正確了WorkId,不信你可以寫個例子,思路是:10個并發同時生成Id,保存到安全執行緒字典中,重復就報個錯,
- 雪花演算法需要額外維護WorkId的作業,
- 有時鐘回撥問題,
Guid生成原始碼地址:MySqlSequentialGuidValueGenerator.cs SqlServerSequentialGuidValueGenerator.cs
使用方式:EF7中:創建模型,生成的值,(***后面會繼續講到)
復合鍵
復合鍵是指將多個列作為主鍵的一種設計模式,這些列在組合起來時才能唯一標識一條記錄,相對于單一鍵,復合鍵更加靈活,可以更準確地描述物體之間的關系,例如,在一個訂單系統中,一個訂單可能由多個產品組成,此時可以使用復合鍵來標識訂單編號和產品編號的組合,以確保每個訂單中的每個產品都是唯一的,
使用復合鍵的優點主要有兩點,首先,它提供了更準確的資料描述,特別是在處理多對多關系時,可以更準確地表示關系的唯一性,其次,使用復合鍵可以提高查詢效率,因為復合鍵可以利用資料庫的索引機制,快速定位和訪問資料,
然而,復合鍵也存在一些缺點,首先,它增加了開發的復雜性,需要更多的設計和規劃,其次,在使用ORM框架時,如EF7,復合鍵的使用需要特殊的處理,例如在Fluent API中進行配置,最后,復合鍵在某些情況下可能會導致性能問題,例如在大型資料庫中,使用復合鍵可能會影響查詢性能,
在使用EF7時,可以通過Fluent API來配置復合鍵,以一個用戶角色表為例,可以使用以下代碼定義一個由用戶Id和角色Id的復合主鍵:
internal class UserRole
{
public string UserId { get; set; }
public string RoleId { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserRole>().HasKey(c => new { c.UserId, c.RoleId });
}
}
在這個示例中,使用Fluent API中的HasKey方法來定義復合主鍵,由于復合鍵是由多個屬性組成的,因此需要將它們放在一個匿名型別中作為引數傳遞給HasKey方法,
注意合理的復合鍵會提高性能,
MySql為例:當使用復合鍵并且確保關聯資料都已經存在時,插入新資料時可能不會對性能產生太大影響,這是因為在 MySQL 中,使用復合鍵時,MySQL 會同時使用所有列生成 B-tree 索引,從而提高查詢和插入的性能,在插入資料時,如果已經確保了關聯資料的存在,那么 MySQL 可以更快速地插入新資料并生成新的索引,從而提高插入性能,
但是,需要注意,如果你的表結構復雜,或者在插入資料時沒有正確使用索引,那么復合鍵仍然可能會影響插入性能,
備選鍵
為什么要使用EF7備選鍵?
在資料庫中,主鍵通常用于唯一標識和檢索物體物件,但是,有些情況下可能需要使用備選鍵來標識物體物件,例如,當主鍵不適合用作某些查詢時,使用備選鍵可以提高資料庫的性能和靈活性,
優點:
- 提高性能:使用備選鍵可以減少復雜查詢中的連接數量和查詢時間,從而提高資料庫的性能,
- 增強靈活性:備選鍵允許使用其他屬性作為查詢條件,這樣就可以更靈活地查詢資料庫中的物體物件,
- 減少沖突:使用備選鍵可以避免主鍵沖突的情況,尤其是在多個物體物件使用同一主鍵的情況下,
缺點:
- 增加復雜性:使用備選鍵會增加代碼的復雜性,因為需要額外的配置和代碼來實作備選鍵的功能,
- 增加維護成本:使用備選鍵會增加資料庫的維護成本,因為需要更多的索引和查詢,以及更多的代碼來處理備選鍵,
- 影響資料完整性:如果備選鍵沒有正確配置,可能會導致資料完整性的問題,因為重復的備選鍵可能會導致資料重復或丟失,
為什么有這些優點和缺點?
優點是因為備選鍵可以提供更靈活、更高效的查詢和更少的主鍵沖突,缺點是因為使用備選鍵需要更多的配置和代碼,并且可能會影響資料完整性,
以下是一個簡單的示例,演示如何使用備選鍵:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class MyContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 將Email屬性指定為備選鍵
modelBuilder.Entity<Person>().HasAlternateKey(p => p.Email);
}
}
復合備選鍵
為什么要使用復合備選鍵?
使用復合備選鍵的一個重要原因是在某些情況下,單個列可能不足以唯一標識表中的每一行,例如,在一個電影資料庫中,如果只使用電影名稱作為主鍵,則會出現多個電影名稱相同的情況,因此,需要使用復合備選鍵,通過多個列來確定每個電影的唯一性,
優點
- 更加靈活的資料建模:復合備選鍵使得資料建模更加靈活,可以在表中使用多個列來確定唯一性,這使得資料建模更加符合實際情況,可以更好地支持復雜的業務場景,
- 更好的性能:使用復合備選鍵可以提高資料庫的性能,這是因為使用多個列來唯一標識行,可以減少在表中的掃描次數,從而提高查詢性能,
- 更好的資料完整性:使用復合備選鍵可以更好地保證資料完整性,在使用復合備選鍵的表中,每個行的唯一性都由多個列決定,這使得在資料插入和更新時,更難發生資料沖突和錯誤,
缺點
- 復雜性:使用復合備選鍵會增加表的復雜性,需要在表中定義多個列,以確定唯一性,此外,在查詢時,需要指定多個列作為條件,以獲取唯一的行,這可能會增加代碼的復雜性,需要更加謹慎地撰寫代碼,
- 不支持自動增長:使用復合備選鍵時,不支持自動增長功能,這是因為每個行的唯一性都是由多個列來決定的,如果一個列是自動增長的,就不能保證每一行都是唯一的,
使用方式
使用Fluent API是定義復合備選鍵的最佳方式,以下是使用Fluent API定義復合備選鍵的示例:
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
}
public class PersonContext : DbContext
{
public DbSet<Person> Persons { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
// 將多個屬性配置為備選鍵(即復合備選鍵)
.HasAlternateKey(p => new { p.FirstName, p.LastName, p.DateOfBirth })
// 可配置備選鍵的索引和唯一約束的名稱:
.HasName("FirstName_LastName_DateOfBirth");
}
}
分組配置模型
可以使用分組配置,這樣可以將物體和關系的配置組織在一起,使代碼更具可讀性,例如:
public class BlogConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
builder.HasKey(x => x.BlogId);
builder.Property(x => x.Name).IsRequired();
}
}
然后在DbContext中使用以下代碼將此配置應用于Blog物體:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new BlogConfiguration());
}
使用資料注釋來配置模型
可以使用資料注釋來配置EF7模型,資料注釋是一種以屬性和類注釋的方式來提供元資料的方法,例如:
public class Blog
{
[Key]
public int BlogId { get; set; }
[Required]
public string Name { get; set; }
}
在這個示例中,我們使用了Key和Required注釋來指定BlogId和Name屬性的主鍵和IsRequired標志,
內置約定
除了手動配置外,EF7還提供了一些內置約定,可以根據慣例自動推斷模型,例如,如果物體中有一個名為Id的屬性,EF7會將其作為主鍵,如果物體之間具有參考關系,EF7會自動創建外鍵,
洗掉現有約定
如果不想使用內置約定,可以通過呼叫ModelBuilder.Conventions.Remove方法來洗掉它們,例如,以下代碼洗掉了為外鍵列創建索引的約定:
modelBuilder.Conventions.Remove<ForeignKeyIndexConvention>();
總結:
在本文中,介紹了如何告訴EF7使用物體型別和過濾型別,并且還說了共享物體型別,我們還介紹了物體屬性的配置,還介紹了四種鍵,介紹了使用Fluent API和資料注釋來配置EF7模型,我們還了解了EF7的內置約定,并學習了如何洗掉現有約定,使用這些技術,可以輕松地創建和配置EF7模型,并更好地管理資料庫訪問代碼,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/546010.html