65.9K
CodeProject 正在变化。 阅读更多。
Home

Entity Framework 中的继承:每层表

starIconstarIconstarIconstarIconstarIcon

5.00/5 (9投票s)

2014 年 7 月 11 日

CPOL

5分钟阅读

viewsIcon

40220

这是展示 Entity Framework 中继承的第一个文章。在本文中,我实现了“表分层”方法。

引言

这是 Entity Framework 文章系列。在我们之前的两篇文章中,我们学习了使用 Entity Framework 的各种方法以及在代码优先方法中处理数据库的各种策略。您可以在这里阅读:

在本文中,我们将学习如何在 Entity Framework 中处理继承。如果您对文章的目标不清楚,以下内容将有助于您理解:

我们知道继承是面向对象编程中的一个优秀概念,它提供了可重用的概念,这是软件设计原则最重要的实践之一。好的,我们将实现类级别的继承,这很好,但我们如何在数据库级别实现它呢?通常,我们知道派生类包含基类的所有属性,那么派生类表是否会包含基类表的所有属性?

我们将在这篇文章以及接下来的两篇文章中学习这些内容。在 Entity Framework 层面,有三种方法可以处理代码级别的继承。

  • 表分层
  • 类型分层
  • 表分具体类型

在本文中,我们将理解“表分层”。我们知道继承只不过是一个分层概念,其中一个类从另一个类派生。它通过类和子类形成一个树状结构。表分层概念的意思是,每个分层只会创建一个表。例如,如果有一个类 A,并且类 B 从类 A 派生,那么对于该分层,数据库中只会创建一个表。所以基本概念是,一个分层将有一个表。看看下面的代码。

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace CodeFirst
{
    public class Person
    {
        [Key]
        public int PersonId { get; set; }
        [Required]
        [MaxLength(10)]
        public string Name { get; set; }
        public string surname { get; set; }
    }

    public enum FriendType
    {
        Close,
        NotClose,
        FamilyFriend
    }

    public class Friend : Person
    {
        public Int32 Person { get; set; }
        public FriendType FriendType { get; set; }
    }


    public class TestContext : DbContext
    {
        public TestContext()
            : base("DBConnectionString")
        {

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

}

在此示例中,我们通过在 Friend 类中继承 Person 类来实现了一个分层。Entity Framework 足以在没有任何外部指令的情况下实现表分层。

现在让我们看看 TestContext 类。我们通过构造函数传递连接字符串,连接字符串需要在 web.config 文件中进行配置。在该类中,我们只初始化了 Person 类,而没有初始化 Friend 类。这就是 Entity Framework 的优势。如果我们填充基类,那么相关的派生类也会自动配置到一个表中。这是此示例在 web.config 文件中的连接字符串。

  <connectionStrings>
    <add name="DBConnectionString"
    connectionString="Data Source=SERVERNAME;Initial Catalog=PersonDB;Integrated Security=true"
    providerName="System.Data.SqlClient"/>
  </connectionStrings>

一旦我们运行应用程序,我们将看到数据库已创建,并且表已生成,如下图所示。

如果我们仔细查看表结构,我们会看到一个 Discriminator 列。为什么创建了这个额外的列?我们在代码中没有实现它。Entity Framework 聪明地创建了该字段来区分不同的表。概念是这样的:Person 对象和 Friend 对象都将保存在同一个表中,那么我们如何区分它们呢?我们如何知道特定行中的数据是用于 Person 对象还是 Friend 对象?为了区分它们,引入了 Discriminator 列。这是我们保存 Person 类对象的地方

		static void Main(string[] args)
        {
            using (var ctx = new TestContext())
            {
                //Save Object of Person table.
                ctx.Person.Add(new Person { Name = "Sourav", surname = "Kayal" });
                ctx.SaveChanges();
            }
        }

看看表数据,我们看到“Person”已保存在 Discriminator 列的值中,所以记录包含 Person 类的对象。

现在,我们将在同一个表中保存 Friend 类的对象。看看下面的代码

static void Main(string[] args)
        {
            using (var ctx = new TestContext())
            {
                //Save Object of Person table.
                ctx.Person.Add(new Friend { Name = "Foo", surname = "Bar" ,PersonId = 1,FriendType =FriendType.Close});
                ctx.SaveChanges();
            }
        }

这里数据已保存在表中,并且 Discriminator 列的值为 Friend。

自定义“Discriminator”列

您可能会想:是否可以更改默认的“Discriminator”列名?或者更改其值?是的,这是可能的。使用 Fluent API 我们可以做到。看看修改后的代码。

public class TestContext : DbContext
    {
        public TestContext()
            : base("DBConnectionString")
        {
        }
        public DbSet<Person> Person { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Person>()
                        .Map<Friend>(m => m.Requires("ObjectType").HasValue("FriendType"));

            modelBuilder.Entity<Person>()
                        .Map<Person>(m => m.Requires("ObjectType").HasValue("PersonType"));
        }

    }

现在,当我们保存某些对象的时。例如,Friend 对象将保存值“Friend Type”,与之前的示例中的“Friend”不同。让我们尝试使用下面的代码保存数据。

 static void Main(string[] args)
        {
            using (var ctx = new TestContext())
            {
                ctx.Person.Add(new Friend { Name = "Foo", surname = "Bar" ,PersonId = 1,FriendType =FriendType.Close});
                ctx.SaveChanges();
            }
        }

看看数据库结构。我们看到“FriendType”已保存在表中,因为我们保存了 Friend 类的对象。

现在,我们将尝试在表中保存 Person 类的对象。这是一个示例代码

static void Main(string[] args)
{
            using (var ctx = new TestContext())
            {
                ctx.Person.Add(new Person { Name = "Ram", surname = "Kumar" });
                ctx.SaveChanges();
            }
}

现在,表中显示的值是“Person Type”。

所以我们已经了解了表的创建概念和 Discriminator 列的概念,现在我们将学习从表中提取数据。要从这样的表中提取数据,我们可以使用多态查询和非多态查询。尝试一下

多态查询

根据概念,它将在单个查询中拉取所有相关的分层数据。在这里,我们拉取所有与 Person 对象相关的数据。现在,由于 Friend 是从 Person 派生的,Friend 的信息会自动拉取。这是一个示例代码。

			using (var ctx = new TestContext())
            {
                var data = from tab in ctx.Person select tab;
                List<Person> persons= data.ToList();
            }

看到表中的两条记录已被提取,所以我们同时获得了 FriendPerson 对象。

非多态查询

在此查询中,我们将提取一个特定的列值。这里我们只拉取“Friend”对象。

	using (var ctx = new TestContext())
       {
                //Get All Friend From Person Table
                var data = from tab in ctx.Person.OfType<Friend>() select tab;
                List<Friend> persons = data.ToList();

       }

我们只得到一个对象。

总结

我们已经学习了如何在 Entity Framework 中实现“表分层”概念来处理继承。希望本文能帮助您在 Entity Framework 的学习道路上更进一步。在接下来的文章中,我们将理解 Entity Framework 中的另外两种继承策略。环境。

© . All rights reserved.