亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Entity?Framework使用Code?First的實(shí)體繼承模式

 更新時(shí)間:2022年03月05日 11:02:58   作者:.NET開(kāi)發(fā)菜鳥(niǎo)  
本文詳細(xì)講解了Entity?Framework使用Code?First的實(shí)體繼承模式,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

Entity Framework的Code First模式有三種實(shí)體繼承模式

1、Table per Type (TPT)繼承

2、Table per Class Hierarchy(TPH)繼承

3、Table per Concrete Class (TPC)繼承

一、TPT繼承模式

當(dāng)領(lǐng)域?qū)嶓w類有繼承關(guān)系時(shí),TPT繼承很有用,我們想把這些實(shí)體類模型持久化到數(shù)據(jù)庫(kù)中,這樣,每個(gè)領(lǐng)域?qū)嶓w都會(huì)映射到單獨(dú)的一張表中。這些表會(huì)使用一對(duì)一關(guān)系相互關(guān)聯(lián),數(shù)據(jù)庫(kù)會(huì)通過(guò)一個(gè)共享的主鍵維護(hù)這個(gè)關(guān)系。

假設(shè)有這么一個(gè)場(chǎng)景:一個(gè)組織維護(hù)了一個(gè)部門(mén)工作的所有人的數(shù)據(jù)庫(kù),這些人有些是拿著固定工資的員工,有些是按小時(shí)付費(fèi)的臨時(shí)工,要持久化這個(gè)場(chǎng)景,我們要?jiǎng)?chuàng)建三個(gè)領(lǐng)域?qū)嶓w:Person、Employee和Vendor類。Person類是基類,另外兩個(gè)類會(huì)繼承自Person類。實(shí)體類結(jié)構(gòu)如下:

1、Person類

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

namespace TPTPattern.Model
{
    public class Person
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public string Email { get; set; }

        public string PhoneNumber { get; set; }
    }
}

Employee類結(jié)構(gòu)

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TPTPattern.Model
{
    [Table("Employee")]
    public class Employee :Person
    {
        /// <summary>
        /// 薪水
        /// </summary>
        public decimal Salary { get; set; }
    }
}

Vendor類結(jié)構(gòu)

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TPTPattern.Model
{
    [Table("Vendor")]
    public class Vendor :Person
    {
        /// <summary>
        /// 每小時(shí)的薪水
        /// </summary>
        public decimal HourlyRate { get; set; }
    }
}

在VS中的類圖如下:

對(duì)于Person類,我們使用EF的默認(rèn)約定來(lái)映射到數(shù)據(jù)庫(kù),而對(duì)Employee和Vendor類,我們使用了數(shù)據(jù)注解,將它們映射為我們想要的表名。

然后我們需要?jiǎng)?chuàng)建自己的數(shù)據(jù)庫(kù)上下文類,數(shù)據(jù)庫(kù)上下文類定義如下:

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

namespace TPTPattern.EFDatabaseContext
{
    public class EFDbContext :DbContext
    {
        public EFDbContext()
            : base("name=Default")
        { }

        public DbSet<Person> Persons { get; set; }
    }
}

在上面的上下文中,我們只添加了實(shí)體類Person的DbSet,沒(méi)有添加另外兩個(gè)實(shí)體類的DbSet。因?yàn)槠渌膬蓚€(gè)領(lǐng)域模型都是從這個(gè)模型派生的,所以我們也就相當(dāng)于將其它兩個(gè)類添加到了DbSet集合中了,這樣EF會(huì)使用多態(tài)性來(lái)使用實(shí)際的領(lǐng)域模型。當(dāng)然,也可以使用Fluent API和實(shí)體伙伴類來(lái)配置映射細(xì)節(jié)信息。

2、使用數(shù)據(jù)遷移創(chuàng)建數(shù)據(jù)庫(kù)

使用數(shù)據(jù)遷移創(chuàng)建數(shù)據(jù)庫(kù)后查看數(shù)據(jù)庫(kù)表結(jié)構(gòu):

在TPT繼承中,我們想為每個(gè)領(lǐng)域?qū)嶓w類創(chuàng)建單獨(dú)的一張表,這些表共享一個(gè)主鍵。因此生成的數(shù)據(jù)庫(kù)關(guān)系圖表如下:

3、填充數(shù)據(jù)

現(xiàn)在我們使用這些領(lǐng)域?qū)嶓w來(lái)創(chuàng)建一個(gè)Employee和Vendor類來(lái)填充數(shù)據(jù),Program類定義如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TPTPattern.EFDatabaseContext;
using TPTPattern.Model;

namespace TPTPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new EFDbContext())
            {
                Employee emp = new Employee()
                {
                  Name="李白",
                  Email="LiBai@163.com",
                   PhoneNumber="18754145782",
                   Salary=2345m
                };

                Vendor vendor = new Vendor()
                {
                   Name="杜甫",
                   Email="DuFu@qq.com",
                   PhoneNumber="18234568123",
                   HourlyRate=456m
                };

                context.Persons.Add(emp);
                context.Persons.Add(vendor);
                context.SaveChanges();
            }

            Console.WriteLine("信息錄入成功");
        }
    }
}

查詢數(shù)據(jù)庫(kù)填充后的數(shù)據(jù):

我們可以看到每個(gè)表都包含單獨(dú)的數(shù)據(jù),這些表之間都有一個(gè)共享的主鍵。因而這些表之間都是一對(duì)一的關(guān)系。

注:TPT模式主要應(yīng)用在一對(duì)一模式下。

二、TPH模式

當(dāng)領(lǐng)域?qū)嶓w有繼承關(guān)系時(shí),但是我們想將來(lái)自所有的實(shí)體類的數(shù)據(jù)保存到單獨(dú)的一張表中時(shí),TPH繼承很有用。從領(lǐng)域?qū)嶓w的角度,我們的模型類的繼承關(guān)系仍然像上面的截圖一樣:

但是從數(shù)據(jù)庫(kù)的角度講,應(yīng)該只有一張表保存數(shù)據(jù)。因此,最終生成的數(shù)據(jù)庫(kù)的樣子應(yīng)該是下面這樣的:

注意:從數(shù)據(jù)庫(kù)的角度看,這種模式很不優(yōu)雅,因?yàn)槲覀儗o(wú)關(guān)的數(shù)據(jù)保存到了單張表中,我們的表是不標(biāo)準(zhǔn)的。如果我們使用這種方法,那么總會(huì)存在null值的冗余列。

1、創(chuàng)建有繼承關(guān)系的實(shí)體類

現(xiàn)在我們創(chuàng)建實(shí)體類來(lái)實(shí)現(xiàn)該繼承,注意:這次創(chuàng)建的三個(gè)實(shí)體類和之前創(chuàng)建的只是沒(méi)有了類上面的數(shù)據(jù)注解,這樣它們就會(huì)映射到數(shù)據(jù)庫(kù)的單張表中(EF會(huì)默認(rèn)使用父類的DbSet屬性名或復(fù)數(shù)形式作為表名,并且將派生類的屬性映射到那張表中),類結(jié)構(gòu)如下:

2、創(chuàng)建數(shù)據(jù)上下文

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

namespace TPHPattern.EFDatabaseContext
{
    public class EFDbContext :DbContext
    {
        public EFDbContext()
            : base("name=Default")
        { }

        public DbSet<Person> Persons { get; set; }

        public DbSet<Employee> Employees { get; set; }

        public DbSet<Vendor> Vendors { get; set; }
    }
}

3、使用數(shù)據(jù)遷移創(chuàng)建數(shù)據(jù)庫(kù)

使用數(shù)據(jù)遷移生成數(shù)據(jù)庫(kù)以后,會(huì)發(fā)現(xiàn)數(shù)據(jù)庫(kù)中只有一張表,而且三個(gè)實(shí)體類中的字段都在這張表中了, 創(chuàng)建后的數(shù)據(jù)庫(kù)表結(jié)構(gòu)如下:

注意:查看生成的表結(jié)構(gòu),會(huì)發(fā)現(xiàn)生成的表中多了一個(gè)Discriminator字段,它是用來(lái)找到記錄的實(shí)際類型,即從Person表中找到Employee或者Vendor。

4、不使用默認(rèn)生成的區(qū)別多張表的類型

使用Fluent API,修改數(shù)據(jù)上下文類,修改后的類定義如下:

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

namespace TPHPattern.EFDatabaseContext
{
    public class EFDbContext :DbContext
    {
        public EFDbContext()
            : base("name=Default")
        { }

        public DbSet<Person> Persons { get; set; }

        public DbSet<Employee> Employees { get; set; }

        public DbSet<Vendor> Vendors { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // 強(qiáng)制指定PersonType是鑒別器 1代表全職職員 2代表臨時(shí)工
            modelBuilder.Entity<Person>()
                .Map<Employee>(m => m.Requires("PersonType").HasValue(1))
                .Map<Vendor>(m => m.Requires("PersonType").HasValue(2));
            base.OnModelCreating(modelBuilder);
        }
    }
}

重新使用數(shù)據(jù)遷移把實(shí)體持久化到數(shù)據(jù)庫(kù),持久化以后的數(shù)據(jù)庫(kù)表結(jié)構(gòu):

生成的PersonType列的類型是int。

5、填充數(shù)據(jù)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TPHPattern.EFDatabaseContext;
using TPHPattern.Model;

namespace TPHPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new EFDbContext())
            {
                Employee emp = new Employee()
                {
                    Name = "李白",
                    Email = "LiBai@163.com",
                    PhoneNumber = "18754145782",
                    Salary = 2345m
                };

                Vendor vendor = new Vendor()
                {
                    Name = "杜甫",
                    Email = "DuFu@qq.com",
                    PhoneNumber = "18234568123",
                    HourlyRate = 456m
                };

                context.Persons.Add(emp);
                context.Persons.Add(vendor);
                context.SaveChanges();
            }

            Console.WriteLine("信息錄入成功");
        }
    }
}

6、查詢數(shù)據(jù)

注意:TPH模式和TPT模式相比,TPH模式只是少了使用數(shù)據(jù)注解或者Fluent API配置子類的表名。因此,如果我們沒(méi)有在具有繼承關(guān)系的實(shí)體之間提供確切的配置,那么EF會(huì)默認(rèn)將其對(duì)待成TPH模式,并把數(shù)據(jù)放到單張表中。

三、TPC模式

當(dāng)多個(gè)領(lǐng)域?qū)嶓w類派生自一個(gè)基類實(shí)體,并且我們想將所有具體類的數(shù)據(jù)分別保存在各自的表中,以及抽象基類實(shí)體在數(shù)據(jù)庫(kù)中沒(méi)有對(duì)應(yīng)的表時(shí),使用TPC繼承模式。實(shí)體模型還是和之前的一樣。

然而,從數(shù)據(jù)庫(kù)的角度看,只有所有具體類所對(duì)應(yīng)的表,而沒(méi)有抽象類對(duì)應(yīng)的表。生成的數(shù)據(jù)庫(kù)如下圖:

1、創(chuàng)建實(shí)體類

創(chuàng)建領(lǐng)域?qū)嶓w類,這里Person基類應(yīng)該是抽象的,其他的地方都和上面一樣:

2、配置數(shù)據(jù)上下文

接下來(lái)就是應(yīng)該配置數(shù)據(jù)庫(kù)上下文了,如果我們只在數(shù)據(jù)庫(kù)上下文中添加了Person的DbSet泛型屬性集合,那么EF會(huì)當(dāng)作TPH繼承處理,如果我們需要實(shí)現(xiàn)TPC繼承,那么還需要使用Fluent API來(lái)配置映射(當(dāng)然也可以使用配置伙伴類),數(shù)據(jù)庫(kù)上下文類定義如下:

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

namespace TPCPattern.EFDatabaseContext
{
    public class EFDbContext :DbContext
    {
        public EFDbContext()
            : base("name=Default")
        { }

        public virtual DbSet<Person> Persons { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            //MapInheritedProperties表示繼承以上所有的屬性
            modelBuilder.Entity<Employee>().Map(m =>
            {
                m.MapInheritedProperties();
                m.ToTable("Employees");
            });
            modelBuilder.Entity<Vendor>().Map(m =>
            {
                m.MapInheritedProperties();
                m.ToTable("Vendors");
            });
            base.OnModelCreating(modelBuilder);
        }
    }
}

上面的代碼中,MapInheritedProperties方法將繼承的屬性映射到表中,然后我們根據(jù)不同的對(duì)象類型映射到不同的表中。

3、使用數(shù)據(jù)遷移生成數(shù)據(jù)庫(kù)

生成的數(shù)據(jù)庫(kù)表結(jié)構(gòu)如下:

查看生成的表結(jié)構(gòu)會(huì)發(fā)現(xiàn),生成的數(shù)據(jù)庫(kù)中只有具體類對(duì)應(yīng)的表,而沒(méi)有抽象基類對(duì)應(yīng)的表。具體實(shí)體類對(duì)應(yīng)的表中有所有抽象基類里面的字段。

4、填充數(shù)據(jù)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TPCPattern.EFDatabaseContext;
using TPCPattern.Model;

namespace TPCPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new EFDbContext())
            {
                Employee emp = new Employee()
                {
                    Name = "李白",
                    Email = "LiBai@163.com",
                    PhoneNumber = "18754145782",
                    Salary = 2345m
                };

                Vendor vendor = new Vendor()
                {
                    Name = "杜甫",
                    Email = "DuFu@qq.com",
                    PhoneNumber = "18234568123",
                    HourlyRate = 456m
                };

                context.Persons.Add(emp);
                context.Persons.Add(vendor);
                context.SaveChanges();
            }

            Console.WriteLine("信息錄入成功");
        }
    }
}

查詢數(shù)據(jù)庫(kù):

注意:雖然數(shù)據(jù)是插入到數(shù)據(jù)庫(kù)了,但是運(yùn)行程序時(shí)也出現(xiàn)了異常,異常信息見(jiàn)下圖。出現(xiàn)該異常的原因是EF嘗試去訪問(wèn)抽象類中的值,它會(huì)找到兩個(gè)具有相同Id的記錄,然而Id列被識(shí)別為主鍵,因而具有相同主鍵的兩條記錄就會(huì)產(chǎn)生問(wèn)題。這個(gè)異常清楚地表明了存儲(chǔ)或者數(shù)據(jù)庫(kù)生成的Id列對(duì)TPC繼承無(wú)效。

如果我們想使用TPC繼承,那么要么使用基于GUID的Id,要么從應(yīng)用程序中傳入Id,或者使用能夠維護(hù)對(duì)多張表自動(dòng)生成的列的唯一性的某些數(shù)據(jù)庫(kù)機(jī)制。

到此這篇關(guān)于Entity Framework使用Code First實(shí)體繼承模式的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • .Net Web Api中利用FluentValidate進(jìn)行參數(shù)驗(yàn)證的方法

    .Net Web Api中利用FluentValidate進(jìn)行參數(shù)驗(yàn)證的方法

    最近在做Web API,用到了流式驗(yàn)證,就簡(jiǎn)單的說(shuō)說(shuō)這個(gè)流式驗(yàn)證,下面這篇文章主要給大家介紹了關(guān)于.Net Web Api中利用FluentValidate進(jìn)行參數(shù)驗(yàn)證的相關(guān)資料,,需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • 詳解ASP.NET Core 2.0 路由引擎之網(wǎng)址生成(譯)

    詳解ASP.NET Core 2.0 路由引擎之網(wǎng)址生成(譯)

    這篇文章主要介紹了詳解ASP.NET Core 2.0 路由引擎之網(wǎng)址生成(譯),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • WPF實(shí)現(xiàn)簡(jiǎn)單的跑馬燈效果

    WPF實(shí)現(xiàn)簡(jiǎn)單的跑馬燈效果

    這篇文章主要為大家詳細(xì)介紹了WPF實(shí)現(xiàn)簡(jiǎn)單的跑馬燈效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • 詳解ASP.NET配置文件Web.config

    詳解ASP.NET配置文件Web.config

    這篇文章主要介紹了ASP.NET配置文件Web.config,Web.config是asp.net應(yīng)用程序中一個(gè)很重要的配置文件,需要的朋友可以參考下
    2015-10-10
  • 如何在ASP.NET Core類庫(kù)項(xiàng)目中讀取配置文件詳解

    如何在ASP.NET Core類庫(kù)項(xiàng)目中讀取配置文件詳解

    這篇文章主要給大家介紹了關(guān)于如何在ASP.NET Core類庫(kù)項(xiàng)目中讀取配置文件的相關(guān)資料,這是朋友提的一個(gè)問(wèn)題,文中通過(guò)示例代碼介紹的非常詳解,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起看看吧。
    2017-10-10
  • Discuz .net版本中的短消息系統(tǒng)

    Discuz .net版本中的短消息系統(tǒng)

    Discuz .net 短消息實(shí)現(xiàn)原理。
    2009-04-04
  • 在ASP.NET Core5.0中訪問(wèn)HttpContext的方法步驟

    在ASP.NET Core5.0中訪問(wèn)HttpContext的方法步驟

    這篇文章主要介紹了在ASP.NET Core5.0中訪問(wèn)HttpContext的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • asp.net控件DataList分頁(yè)用法

    asp.net控件DataList分頁(yè)用法

    這篇文章主要介紹了asp.net控件DataList分頁(yè)用法,實(shí)例分析了asp.net使用DataList控件實(shí)現(xiàn)分頁(yè)功能的設(shè)置與數(shù)據(jù)操作技巧,需要的朋友可以參考下
    2016-05-05
  • 淺談ASP.NET Core的幾種托管方式

    淺談ASP.NET Core的幾種托管方式

    這篇文章主要介紹了淺談ASP.NET Core的幾種托管方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • aspnet_regsql.exe 工具注冊(cè)數(shù)據(jù)庫(kù)的圖文方法

    aspnet_regsql.exe 工具注冊(cè)數(shù)據(jù)庫(kù)的圖文方法

    自 ASP.NET 2.0 起,微軟在 ASP.NET 上新增了很多功能,其中包括 Membership , Role , Profile 等等諸多功能
    2010-03-03

最新評(píng)論