文章出處

前面,我們已經了解了Code-First利用領域類,怎么為我們創建數據庫的簡單示例。現在我們來學習一下Code-First約定吧。

  • 什么是約定

約定說白了,就是基于一套規矩辦事,這里就是基于你定義好的領域類,然后根據默認的規矩來配置概念模型。Code-First約定定義在這個命名空間下面:

System.Data.Entity.ModelConfiguration.Conventions

 

現在來大致瀏覽一下都有哪些約定吧:

  • 類型發現

在前面的章節中,我們在上下文類中創建了DbSet屬性的類集合,然后Code-First會根據這個DbSet屬性為我們創建數據表。

codeFirst會為我們包含任何引用類型到這些類集合中,甚至盡管這寫引用類型定義在不同的程序集中。

例如:下面的代碼中,Student實體包含了Teacher類的引用,然而上下文類總,卻并未包含Teacher的類型的DBSet屬性。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EF1
{
   public class Student
    {
        public int StudentId { get; set; }

        public string StudentName { get; set; }

        public DateTime DateOfBirth { get; set; }
        public byte[] Photo { get; set; }
        public decimal Height { get; set; }
        public float Weight { get; set; }

        public Standard Standard { get; set; }

        public Teacher Teacher { get; set; }

    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EF1
{
  public  class Teacher
    {
      public int TeacherId { get; set; }

      public string TeacherName { get; set; }
    }
}

數據上下文類中,并沒有包含Teacher類型的DbSet屬性:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EF1
{
   public class DbContextClass:DbContext
    {
       public DbContextClass()
           : base("ConnectionString")
       { }
       public DbSet<Student> Studnets { get; set; }

       public DbSet<Standard> Standards { get; set; }


    }
}

然后我們運行程序,運行成功之后,打開數據庫,發現:

哈哈,是不是報錯了?

具體的錯誤消息是:The model backing the 'DbContextClass' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269)

這是因為,我們改了實體,沒有開啟數據庫遷移特性,不過還有另外一種解決辦法,我這里,

我們改一下上下文類:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EF1
{
   public class DbContextClass:DbContext
    {
       public DbContextClass()
           : base("ConnectionString")
       { }
       public DbSet<Student> Studnets { get; set; }

       public DbSet<Standard> Standards { get; set; }

       
       protected override void OnModelCreating(DbModelBuilder modelBuilder)
       {
           Database.SetInitializer(new DropCreateDatabaseIfModelChanges<DbContextClass>());

           base.OnModelCreating(modelBuilder);
       }

    }
}

這里改動的語句:

  Database.SetInitializer(new DropCreateDatabaseIfModelChanges<DbContextClass>());

意思是,當模型發生改變的時候,就刪掉重新創建數據庫。

現在我們運行程序,發現就能運行成功了。

然后我們看一下數據庫:

多了一個Teacher表。

看一下Teacher的表結構:

然后看一下Student表的表結構:

Code-First可以推斷類,盡管上下文中并沒有包含Teacher的屬性。

所以,我們總結一下類型發現的約定:

  • Code-First包含類型定義也就是DbSet屬性在上下文類中;Code-First includes types defined as a DbSet property in context class.
  • Code-First包含引用類型在實體類中,盡管他們定義在不同的程序集下;Code-First includes reference types included in entity types even if they are defined in different assembly.
  • Code-First包含派生類,盡管這個類要定義成DbSet屬性。(Code-First includes derived classes even if only the base class is defined as DbSet property.)
  • 主鍵約定

在之前的例子中,我們看見了Code-First為每個表都創建了主鍵,這個默認的規則就是,類名Id(大小寫不敏感,即不區分大小寫)。這個主鍵的類型可以是任何類型,但是如果主鍵的類型是numeric 或者GUID,它將會被配置成自增列。

然后,如果你定義主鍵的屬性名字不是Id或者不是類名+ID,然后在運行的時候,就會報錯。

我們看看下面的代碼就知道了:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EF1
{
   public class Student
    {
        public int StdID { get; set; }

        public string StudentName { get; set; }

        public DateTime DateOfBirth { get; set; }
        public byte[] Photo { get; set; }
        public decimal Height { get; set; }
        public float Weight { get; set; }

        public Standard Standard { get; set; }

        public Teacher Teacher { get; set; }

    }
}

改完之后,我們運行程序,就會報錯:

具體的錯誤消息:

One or more validation errors were detected during model generation:

EF1.Student: : EntityType 'Student' has no key defined. Define the key for this EntityType.
Studnets: EntityType: EntitySet 'Studnets' is based on type 'Student' that has no keys defined.

意思是沒有定義主鍵》》》

如果你想要StdID成為主鍵,可以使用數據注解或者Fluent APIS,我們在后面將會學到。

  • 關系約定

通過導航屬性,Code-First能夠推斷出,兩個實體之間的關系,這個導航屬性可以是簡單的引用類型或者是集合類型,例如,我們在Student實體中定義了Standard導航屬性,然后我們在Stanrard實體中定義了Icollection<Student>導航屬性,所以,Code-First將會自動為我們創建一對多的關系。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EF1
{
   public class Student
    {
        public int StdID { get; set; }

        public string StudentName { get; set; }

        public DateTime DateOfBirth { get; set; }
        public byte[] Photo { get; set; }
        public decimal Height { get; set; }
        public float Weight { get; set; }


       /// <summary>
       /// 導航屬性
       /// </summary>
        public Standard Standard { get; set; }

      

    }
}

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EF1
{
   public class Standard
    {
       public int StandardId { get; set; }

       public string StandardName { get; set; }


       /// <summary>
       /// 集合類型的導航屬性
       /// </summary>
       public ICollection<Student> Students { get; set; }
    }
}

然后我們運行程序成功之后,看到數據庫:

 

Code-First約定,為我們創建了外鍵

<navigation property Name>_<primary key property name of navigation property type> e.g. Standard_StandardId.

  • 外鍵約定

我們已經知道了,當我們添加一個外鍵屬性的時候,Code--First會我們創建一個外鍵。推薦做法是,包含一個外鍵屬性的依賴關系。

請看下面的代碼;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EF1
{
   public class Student
    {
       public int StudentId { get; set; }

        public string StudentName { get; set; }

        public DateTime DateOfBirth { get; set; }
        public byte[] Photo { get; set; }
        public decimal Height { get; set; }
        public float Weight { get; set; }


       /// <summary>
       /// 導航屬性
       /// </summary>
        public Standard Standard { get; set; }

       /// <summary>
       /// Foriegn key  for  Standard
       /// </summary>
        public int StandardId { get; set; }

      

    }
}

這樣改動之后,我們在運行程序,發現怎么了?是不是又報錯了?

 

這個是因為,外鍵表Standard中沒有數據。

我們往Standard表中,插入一條數據,然后改一下Main函數里面的代碼:再運行程序>>>>

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EF1
{
    class Program
    {
        static void Main(string[] args)
        {

            Student stu = new Student() { StudentId = 1, StudentName = "韋小寶", Height = 172, Weight = 120, DateOfBirth = DateTime.Now,StandardId=1};

            using (var db = new DbContextClass())
            {
                db.Studnets.Add(stu);
                db.SaveChanges();
            }

            Console.WriteLine("添加成功");
            Console.ReadKey();
        }
    }
}

 

運行之后,是不是沒有出錯了?

現在讓我們再來看一下數據庫:

發現什么變化了么?是不是這個外鍵的名字不再是以外鍵屬性的名字+外鍵實體的主鍵ID命名了?

并且你注意到了沒有,這個外鍵屬性也是不能為空的?

因為int類型是不能為空的!!

code-Forst可以基于外鍵屬性的【為空性】(nullability )推斷出多重性的關系,如果這個屬性是可以為空的,然后這個關系可以看成是null,否則的話,就是not null,你可以修改

StandardId屬性的類型從int變成Nullable<int>,然后運行程序的話,得到的外鍵就是可以為空了。

  • 復雜類型約定

Code-First可以為類創建復雜類型,可以不包含主鍵屬性,并且不使用數據注解和Fluent APIs來注冊主鍵

 

上面這寫就是Code-First約定的大概內容了,這些約定可以被打破,通過使用數據注解和Fluent APIs,在EF6中,你可以自定義約定。

 

附上系列目錄:

 


文章列表




Avast logo

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


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

    IT工程師數位筆記本

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