文章出處

如果在 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();
    }
}

執行結果:


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()