溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Entity?Framework如何使用Code?First的實體繼承模式

發(fā)布時間:2022-03-05 14:02:59 來源:億速云 閱讀:172 作者:小新 欄目:開發(fā)技術

這篇文章主要介紹Entity Framework如何使用Code First的實體繼承模式,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

Entity Framework的Code First模式有三種實體繼承模式

1、Table per Type (TPT)繼承

2、Table per Class Hierarchy(TPH)繼承

3、Table per Concrete Class (TPC)繼承

一、TPT繼承模式

當領域實體類有繼承關系時,TPT繼承很有用,我們想把這些實體類模型持久化到數(shù)據(jù)庫中,這樣,每個領域實體都會映射到單獨的一張表中。這些表會使用一對一關系相互關聯(lián),數(shù)據(jù)庫會通過一個共享的主鍵維護這個關系。

假設有這么一個場景:一個組織維護了一個部門工作的所有人的數(shù)據(jù)庫,這些人有些是拿著固定工資的員工,有些是按小時付費的臨時工,要持久化這個場景,我們要創(chuàng)建三個領域實體:Person、Employee和Vendor類。Person類是基類,另外兩個類會繼承自Person類。實體類結構如下:

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類結構

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類結構

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>
        /// 每小時的薪水
        /// </summary>
        public decimal HourlyRate { get; set; }
    }
}

在VS中的類圖如下:

Entity?Framework如何使用Code?First的實體繼承模式

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

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

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; }
    }
}

在上面的上下文中,我們只添加了實體類Person的DbSet,沒有添加另外兩個實體類的DbSet。因為其它的兩個領域模型都是從這個模型派生的,所以我們也就相當于將其它兩個類添加到了DbSet集合中了,這樣EF會使用多態(tài)性來使用實際的領域模型。當然,也可以使用Fluent API和實體伙伴類來配置映射細節(jié)信息。

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

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

Entity?Framework如何使用Code?First的實體繼承模式

在TPT繼承中,我們想為每個領域實體類創(chuàng)建單獨的一張表,這些表共享一個主鍵。因此生成的數(shù)據(jù)庫關系圖表如下:

Entity?Framework如何使用Code?First的實體繼承模式

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

現(xiàn)在我們使用這些領域實體來創(chuàng)建一個Employee和Vendor類來填充數(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ù)庫填充后的數(shù)據(jù):

Entity?Framework如何使用Code?First的實體繼承模式

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

注:TPT模式主要應用在一對一模式下。

二、TPH模式

當領域實體有繼承關系時,但是我們想將來自所有的實體類的數(shù)據(jù)保存到單獨的一張表中時,TPH繼承很有用。從領域實體的角度,我們的模型類的繼承關系仍然像上面的截圖一樣:

Entity?Framework如何使用Code?First的實體繼承模式

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

Entity?Framework如何使用Code?First的實體繼承模式

注意:從數(shù)據(jù)庫的角度看,這種模式很不優(yōu)雅,因為我們將無關的數(shù)據(jù)保存到了單張表中,我們的表是不標準的。如果我們使用這種方法,那么總會存在null值的冗余列。

1、創(chuàng)建有繼承關系的實體類

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

Entity?Framework如何使用Code?First的實體繼承模式

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ù)庫

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

Entity?Framework如何使用Code?First的實體繼承模式

注意:查看生成的表結構,會發(fā)現(xiàn)生成的表中多了一個Discriminator字段,它是用來找到記錄的實際類型,即從Person表中找到Employee或者Vendor。

4、不使用默認生成的區(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)
        {
            // 強制指定PersonType是鑒別器 1代表全職職員 2代表臨時工
            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ù)據(jù)庫,持久化以后的數(shù)據(jù)庫表結構:

Entity?Framework如何使用Code?First的實體繼承模式

生成的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ù)

Entity?Framework如何使用Code?First的實體繼承模式

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

三、TPC模式

當多個領域實體類派生自一個基類實體,并且我們想將所有具體類的數(shù)據(jù)分別保存在各自的表中,以及抽象基類實體在數(shù)據(jù)庫中沒有對應的表時,使用TPC繼承模式。實體模型還是和之前的一樣。

然而,從數(shù)據(jù)庫的角度看,只有所有具體類所對應的表,而沒有抽象類對應的表。生成的數(shù)據(jù)庫如下圖:

Entity?Framework如何使用Code?First的實體繼承模式

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

創(chuàng)建領域實體類,這里Person基類應該是抽象的,其他的地方都和上面一樣:

Entity?Framework如何使用Code?First的實體繼承模式

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

接下來就是應該配置數(shù)據(jù)庫上下文了,如果我們只在數(shù)據(jù)庫上下文中添加了Person的DbSet泛型屬性集合,那么EF會當作TPH繼承處理,如果我們需要實現(xiàn)TPC繼承,那么還需要使用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 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ù)不同的對象類型映射到不同的表中。

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

生成的數(shù)據(jù)庫表結構如下:

Entity?Framework如何使用Code?First的實體繼承模式

查看生成的表結構會發(fā)現(xiàn),生成的數(shù)據(jù)庫中只有具體類對應的表,而沒有抽象基類對應的表。具體實體類對應的表中有所有抽象基類里面的字段。

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ù)庫:

Entity?Framework如何使用Code?First的實體繼承模式

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

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

Entity?Framework如何使用Code?First的實體繼承模式

以上是“Entity Framework如何使用Code First的實體繼承模式”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。

AI