1. 程式人生 > >Code First 2

Code First 2

set 實現 更新 復制代碼 pin 截圖 所有 void ucc

在codefirst一中也說了Mapping是實體與數據庫的紐帶,model通過Mapping映射到數據庫,我們可以從數據庫的角度來分析?首先是映射到數據庫,這個是必須的。數據庫裏面一般包括表、列、約束、主外鍵、級聯操作、實體關系(E-R圖)、存儲過程、視圖、鎖、事務、數據庫結構更新等。在接下來的日子裏,通過數據庫的這些名詞,來學習C#是如何實現或者調用它們來通過代碼操作數據庫。

在code first一中主要是學習了對數據庫的映射,今天主要是學習表的映射。一個model映射一個表,多個modle映射一個表,一個model映射多個表,子類映射表。

一、表名的映射

學習model與表的映射肯定要知道表名的映射,可以通過using System.ComponentModel.DataAnnotations.Schema;命名空間下的[Table("TestStudent")]特性來約定表名的映射。

二、一個model映射一個表

這個就不用多說,一般情況都是一個model映射一個表,在code first一中也有Student類映射到Student表。

三、子類映射表

面向對象的三大特征之一就是繼承,model中也會有繼承實體,那子類如何映射到表中呢?

首先定義了一個Person類,一個繼承Person的Student類。

技術分享
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EFCodeFirstModels
{
    public enum SexType { Male, Female }
    
    public class Person
    {
        [Key]
        public string PersonId { get; set; }
        //姓名
        public string Name { get; set; }
        //性別
        public SexType Sex { get; set; }
        //年齡
        public int Age { get; set; }

        public Person(string personId, string name, SexType sex, int age)
        {
            PersonId = personId;
            Name = name;
            Sex = sex;
            Age = age;
        }
    }
}
技術分享 技術分享
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EFCodeFirstModels
{
    public class Student : Person
    {
        public string StuId { get; set; }

        public string Major { get; set; }

        public string School { get; set; }

        public Student(string stuId, string major, string school, string personId, string name, SexType sex, int age) : base(personId, name, sex, age)
        {
            this.StuId = stuId;
            this.Major = major;
            this.School = major;
            this.PersonId = personId;
            this.Name = name;
            this.Sex = sex;
            this.Age = age;
        }
    }

}
技術分享

數據庫上下文還是一默認的

技術分享
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
using EFCodeFirstModels;
using System.Configuration;


namespace EFCodeFirstDataAccess
{
    public class EFCodeFirstDbContext:DbContext
    {

        public EFCodeFirstDbContext() : base("MyStrConn")
        {
        }
        public DbSet<Person> Persons { get; set; }

    }
}
技術分享

在Main中增加一個Person對象,一個Student對象。

技術分享
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EFCodeFirstModels;
using EFCodeFirstDataAccess;

namespace EFCodeFirstDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //using能及時釋放資源,例如數據庫連接異常,可以即使將上下文釋放
            using (var db=new EFCodeFirstDbContext())
            {
                Person person = new Person("1001", "張三", SexType.Female, 26);
                db.Persons.Add(person);
                Student stu = new Student("001", "軟件工程", "藍翔", "1000", "CYW", SexType.Female, 25);
                db.Persons.Add(stu);
                db.SaveChanges();
                Console.WriteLine("Success");
            }
            Console.ReadLine();
            
        }
    }
}
技術分享

運行之後可以看到數據庫生成了一個People表,多了一列:Discriminator,用來區別子類和父類對象

技術分享

如果你想改名Discriminator列名,可以使用Fluent API來設置,需要在數據庫上下文中重寫OnModelCreating()方法。

技術分享
public class EFCodeFirstDbContext:DbContext
    {

        public EFCodeFirstDbContext() : base("MyStrConn")
        {
        }
        public DbSet<Person> Persons { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Person>().Map(m =>
            {
                m.ToTable("Person");
                m.Requires("PersonType").HasValue("Person");
            }).Map<Student>(m =>
            {
                m.Requires("PersonType").HasValue("Student");
            });

        }
    }
技術分享

技術分享

如果Person只有一個派生類,那我們可以使用布爾值來區別

技術分享
            modelBuilder.Entity<Person>().Map(m =>
            {
                m.ToTable("Person");
                m.Requires("IsStudent").HasValue(false);
            }).Map<Student>(m =>
            {
                m.Requires("IsStudent").HasValue(true);
            });
技術分享

技術分享

上面是兩個Model生成一個表,基類和派生類都映射到同一張表中,通過使用鑒別列來識別是否為子類型。這是Code First默認規則使用的表映射方法TPH(Table Per Hierarchy)。其實還有兩種映射方法。一種是TPT,一種是TPC.

TPT:Table Per Type,TPH將所有層次的類都放在了一個表裏,而TPT在一個單獨的表中儲存來自基類的屬性,在派生類定義的附加屬性儲存在另一個表裏,並使用外鍵與主表相連接。這種方式只需指定子類表名。

 [Table("Student")]

技術分享

TPC:Table Per Concrete Type,基類與派生類都映射在不同的表中,不同的是派生類中還包括了基類的字段。TPC只能用Fluent API來配置

技術分享
            modelBuilder.Entity<Person>().Map(m =>
            {
                m.ToTable("Person");
            }).Map<Student>(m =>
            {
                m.ToTable("Student");
                m.MapInheritedProperties();
            });
技術分享

技術分享

四、多個Model映射一個表

上面雖然是兩個類Person、Student映射到一個表中,但主鍵都在基類上定義的,映射一個表還比較容易。如果是兩個沒有繼承關系的Model如何映射到同一張表呢?這個我們在上面Person類Student類的基礎上增加了一個IDCard類,主要是表示身份證類。假設Person的PersonId就是IDCard的PersonId。兩個Model映射一個表,那兩個Molde肯定是一對一的關系,映射的表名相同,主鍵名和類型也要相同,這些是必須的。同時還要指明兩個類的外鍵,只指明一個都不行。

技術分享
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace EFCodeFirstModels
{
    [Table("Person")]
    public class IDCard
    {
        //身份證號
        [Key,ForeignKey("People")]
        public string PersonId { get; set; }

        //法定出生日期
        public DateTime BirthDate { get; set; }

        public Person People { get; set; }

        public IDCard(string personId, DateTime birstDate)
        {
            PersonId = personId;
            BirthDate = birstDate;
        }

    }
}
技術分享 技術分享
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EFCodeFirstModels
{
    public enum SexType { Male, Female }

    [Table("Person")]
    public class Person
    {
        [Key, ForeignKey("Card")]
        public string PersonId { get; set; }
        //姓名
        public string Name { get; set; }
        //性別
        public SexType Sex { get; set; }
        //年齡
        public int Age { get; set; }

        public IDCard Card { get; set; }

        public Person(string personId, string name, SexType sex, int age, IDCard card)
        {
            PersonId = personId;
            Name = name;
            Sex = sex;
            Age = age;
            Card = card;
        }
    }
}
技術分享 技術分享
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EFCodeFirstModels
{
    public class Student : Person
    {
        public string StuId { get; set; }

        public string Major { get; set; }

        public string School { get; set; }

        public Student(string stuId, string major, string school, string personId, string name, SexType sex, int age, IDCard card) : base(personId, name, sex, age, card)
        {
            this.StuId = stuId;
            this.Major = major;
            this.School = major;
            this.PersonId = personId;
            this.Name = name;
            this.Sex = sex;
            this.Age = age;
            this.Card = card;
        }
    }

}
技術分享

在Mian中先添加一個Person來看下運行結果。

技術分享
            //using能及時釋放資源,例如數據庫連接異常,可以即使將上下文釋放
            using (var db=new EFCodeFirstDbContext())
            {

                IDCard card = new IDCard("1000", DateTime.Now);
                Person person = new Person("1001", "張三", SexType.Female, 26, card);
                db.Persons.Add(person);
                db.SaveChanges();
                Console.WriteLine("Success");
            }
            Console.ReadLine();
            
        }
技術分享

技術分享

從上面的截圖看到Person表中也有Student的屬性,感覺真是不可思議,amazing.

我們添加一個Person一個Student來看下效果

技術分享
        static void Main(string[] args)
        {
            //using能及時釋放資源,例如數據庫連接異常,可以即使將上下文釋放
            using (var db=new EFCodeFirstDbContext())
            {

                IDCard card = new IDCard("1000", DateTime.Now);
                Person person = new Person("1001", "張三", SexType.Female, 26, card);
                db.Persons.Add(person);
                IDCard card2 = new IDCard("1000", DateTime.Now);
                Student stu = new Student("001", "挖掘機", "藍翔", "1000", "趙鐵蛋", SexType.Male, 25, card2);
                db.Persons.Add(stu);
                db.SaveChanges();
                Console.WriteLine("Success");
            }
            Console.ReadLine();
            
        }
技術分享

技術分享

IDCard和Person生成了同一表,我們可以像上面的Fluent API來改變Discriminator列名。也可以使用TPT的方式生成兩個表,只需在Student類上指明映射的表名。

技術分享

但是TPC就不行了。就會報錯,這可能也是只新增Person的時候Student中的屬性也會自動添加上的原因吧。

五、一個Model映射多個表

一個Model映射多個表和上面多個Model映射一個表正好反過來,還用上面Student和Person舉例。

技術分享
            modelBuilder.Entity<Student>().Map(m=> {
                m.ToTable("Person");
                m.Properties(p => p.PersonId);
                m.Properties(p=>p.Name);
                m.Properties(p=>p.Age);
                m.Properties(p => p.Sex);
               
                }).Map(m=> {
                    m.ToTable("Student");
                    m.Properties(p => p.PersonId);
                    m.Properties(p=>p.StuId);
                    m.Properties(p => p.Major);
                    m.Properties(p => p.School);

                });
技術分享

上面將一個Student類映射給兩個表一個Student一個person.

技術分享
            //using能及時釋放資源,例如數據庫連接異常,可以即使將上下文釋放
            using (var db=new EFCodeFirstDbContext())
            {

                Person person = new Person("1001", "張三", SexType.Female, 26);
                db.Persons.Add(person);

                Student stu = new Student("001", "挖掘機", "藍翔", "1000", "趙鐵蛋", SexType.Male, 25);
                db.Persons.Add(stu);
                db.SaveChanges();
                Console.WriteLine("Success");
            }
技術分享

Code First 2