如果在 EF OnModelCreating 中配置了實體外鍵映射,也就是 SQL Server 中的 ForeignKey,那么我們在添加實體的時候,主實體的主鍵值會自動映射到子實體的外鍵值,并且這個操作在一個 SaveChanges 中,但如果沒有在 OnModelCreating 中進行外鍵映射配置,我們添加實體的時候,就不會自動映射外鍵值了,什么意思呢?我們先看一個示例代碼:
public class SchoolDbContext : DbContext
{
public SchoolDbContext()
: base("db_school")
{ }
public DbSet<Student> Students { get; set; }
public DbSet<Class> Classs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.HasKey(n => n.StudentId);
modelBuilder.Entity<Class>()
.HasKey(n => n.ClassId);
base.OnModelCreating(modelBuilder);
}
}
public class Student
{
public int StudentId { get; set; }
public int ClassId { get; set; }
public string Name { get; set; }
}
public class Class
{
public int ClassId { get; set; }
public string Name { get; set; }
}
示例很簡單,Class 和 Student 是一對多關系,但我們并沒有在 OnModelCreating 中進行外鍵映射配置,所以生成到 SQL Server 的 db_school 數據庫,會是這樣:
可以看到 Student 中的 ClassId 字段并不是外鍵,下面我們添加 Student 和 Class 實體:
static void Main(string[] args)
{
using (var context = new SchoolDbContext())
{
var entityClass = new Class() { Name = "calss1" };
var entityStudent = new Student() { ClassId = entityClass.ClassId, Name = "student1" };
context.Classs.Add(entityClass);
context.Students.Add(entityStudent);
context.SaveChanges();
}
}
執行結果:
可以看到,Student 表中的 ClassId 值是 0,而并不是我們預想的 1,這是一個問題,在不增加外鍵的情況下,我們一般會這樣解決:
static void Main(string[] args)
{
using (var context = new SchoolDbContext())
{
var entityClass = new Class() { Name = "calss2" };
context.Classs.Add(entityClass);
context.SaveChanges();
var entityStudent = new Student() { ClassId = entityClass.ClassId, Name = "student2" };
context.Students.Add(entityStudent);
context.SaveChanges();
}
}
執行結果:
這種處理方式,雖然“解決”上面的問題,但其實有很多的隱患,多執行一次 SaveChanges,EF 就會多發起一次請求,增加了性能開銷,并且 SaveChanges 是事務性的,如果第一個執行成功了,第二個執行失敗了,這時候第一個事務并不會回滾,因為它獨立于第二個,所以,最后就會造成數據的不一致性,雖然幾率非常點,但我們應該盡量避免。
那有沒有更好的解決方式呢?這個問題我之前有點想復雜了,其實解決非常簡單,就是在 Student 實體中添加 virtual 修飾的 Class 屬性,就可以了,如下:
public class Student
{
public int StudentId { get; set; }
public int ClassId { get; set; }
public string Name { get; set; }
public virtual Class Class { get; set; }//添加屬性
}
需要注意的是,我們并不需要在 OnModelCreating 中進行 Class 和 ClassId 的映射配置,EF 會自動查找 ClassId(屬性名 + Id),所以,“外鍵”命名要注意規范統一,如果命名為 Class_Id 就無效了。
再次執行添加實體的代碼,發現會報錯:
什么意思呢?就是實體更改了,需要進行 EF 遷移,如果你進行 EF 遷移的話,會發現,雖然我們沒有在 OnModelCreating 中進行 ClassId 外鍵映射配置,但 EF 也會自動映射 ForeignKey 到數據庫的,所以代碼命名盡量規范些,EF 是比較“智能”的。
我們解決這個問題的前提條件是“不增加外鍵配置”,所以我們要讓 EF 忽略實體更改:
public SchoolDbContext()
: base("db_school")
{
Database.SetInitializer<SchoolDbContext>(null);//忽略映射
}
再次執行添加實體代碼:
static void Main(string[] args)
{
using (var context = new SchoolDbContext())
{
var entityClass = new Class() { Name = "calss3" };
var entityStudent = new Student() { ClassId = entityClass.ClassId, Name = "student3" };
context.Classs.Add(entityClass);
context.Students.Add(entityStudent);
context.SaveChanges();
}
}
執行結果:
文章列表