zoukankan      html  css  js  c++  java
  • Entity Framework 4.1 之七:继承

    原文名称:Entity Framework 4.1: Inheritance (7)

    原文地址:http://vincentlauzon.wordpress.com/2011/04/19/entity-framework-4-1-inheritance-7/

    看到 Entity Framework 4.1 推荐英文教程,为了帮大家看起来方便一些,简单翻译一下。这是一个系列,共有 8 篇,这是第 7 篇。
     
    1. Entity Framework 4.1 之一 : 基础
    2. Entity Framework 4.1 之二 : 覆盖默认的约定
    3. Entity Framework 4.1 之三 : 贪婪加载和延迟加载
    4. Entity Framework 4.1 之四:复杂类型
    5. Entity Framework 4.1 之五:多对多的关系
    6. Entity Framework 4.1 之六:乐观并发
    7. Entity Framework 4.1 之七:继承
    8. Entity Framework 4.1 之八:绕过 EF 查询映射

    在 ORM 文献中,有三种方式将对象的继承关系映射到表中。

    • 每个类型一张表 TPT: 在继承层次中的每个类都分别映射到数据库中的一张表,彼此之间通过外键关联。
    • 继承层次中所有的类型一张表 TPH:对于继承层次中的所有类型都映射到一张表中,所有的数据都在这张表中。
    • 每种实现类型一张表 TPC: 有点像其他两个的混合,对于每种实现类型映射到一张表,抽象类型像 TPH 一样展开到表中。

    这里我将讨论 TPT 和 TPH,EF 的好处是可以混合使用这些方式。

    TPT 方式

    让我们从每种类型一张表开始,我定义了一个简单的继承层次,一个抽象基类和两个派生类。

    publicabstractclass PersonBase
    {
    publicint PersonID { get; set; }
    [Required]
    publicstring FirstName { get; set; }
    [Required]
    publicstring LastName { get; set; }
    publicint Age { get; set; }
    }

    publicclass Worker : PersonBase
    {
    publicdecimal AnnualSalary { get; set; }
    }

    publicclass Retired : PersonBase
    {
    publicdecimal MonthlyPension { get; set; }
    }

    你需要告诉模型构建器如何映射到表中。

    protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder)
    {
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity
    <PersonBase>().HasKey(x => x.PersonID);
    modelBuilder.Entity
    <PersonBase>().Property(x => x.PersonID)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    // TPT mapping
    modelBuilder.Entity<PersonBase>().ToTable("tpt.Person");
    modelBuilder.Entity
    <Worker>().ToTable("tpt.Worker");
    modelBuilder.Entity
    <Retired>().ToTable("tpt.Retired");
    }

    我们使用默认的命名映射约定,模型构建器使用这些信息用 TPT 来创建数据库。

    我们使用模型来跑一些代码,让我们理解如何使用上面的映射,基本上,我们仅仅使用一个 DbSet,一个 PersonBase 的集合,EF 会管理每一个成员的实际类型。

    publicstaticvoid ManageTPT()
    {
    using (var context1 =new TptContext())
    {
    var worker
    =new Worker
    {
    AnnualSalary
    =20000,
    Age
    =25,
    FirstName
    ="Joe",
    LastName
    ="Plumber"
    };
    var retired
    =new Retired
    {
    MonthlyPension
    =1500,
    Age
    =22,
    FirstName
    ="Mike",
    LastName
    ="Smith"
    };
    // Make sure the tables are empty…
    foreach (var entity in context1.Persons)
    {
    context1.Persons.Remove(entity);
    }
    context1.Persons.Add(worker);
    context1.Persons.Add(retired);

    context1.SaveChanges();
    }
    using (var context2 =new TptContext())
    {
    Console.WriteLine(
    "Persons count: "+ context2.Persons.OfType<PersonBase>().Count());
    Console.WriteLine(
    "Worker: "+ context2.Persons.OfType<Worker>().Count());
    Console.WriteLine(
    "Retired: "+ context2.Persons.OfType<Retired>().Count());
    }
    }

    这真的很强大,我们可以通过访问 Workers 来仅仅访问 Workers 表。

    TPH 方式

    TPH 是 EF 实际上默认支持的。我们可以简单地注释到前面例子中的对表的映射来使用默认的机制。

    protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder)
    {
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity
    <PersonBase>().HasKey(x => x.PersonID);
    modelBuilder.Entity
    <PersonBase>().Property(x => x.PersonID)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    // TPT mapping
    //modelBuilder.Entity<PersonBase>().ToTable("tpt.Person");
    //modelBuilder.Entity<Worker>().ToTable("tpt.Worker");
    //modelBuilder.Entity<Retired>().ToTable("tpt.Retired");
    }

    结果是现在使用一张表来影射整个的继承层次。

    注意到整个的层次被展开到一张表中。基类中没有的属性被自动标记为可空。还有一个额外的区分列,如果运行前面的例子,我们将会看到这个区分列的内容。

    当 EF 读取一行的时候,区分列被 EF 用来知道应该创建实例的类型,因为现在所有的类都被映射到了一张表中。

    也可以覆盖这一点,下面我们看一下同时混合使用 TPH 和 TPT。我定义了 Worker 的两个子类,我希望将这两个类和 Worker 基类映射到一张表。

    publicclass Manager : Worker
    {
    publicint? ManagedEmployeesCount { get; set; }
    }

    publicclass FreeLancer : Worker
    {
    [Required]
    publicstring IncCompanyName { get; set; }
    }

    注意到每一个属性都必须是可空的。这在 TPH 中非常不方便:每一个属性都必须是可空的。现在我们使用模型构建器来完成。

    protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder)
    {
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity
    <PersonBase>().HasKey(x => x.PersonID);
    modelBuilder.Entity
    <PersonBase>().Property(x => x.PersonID)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    // TPT mapping
    modelBuilder.Entity<PersonBase>().ToTable("tpt.Person");
    modelBuilder.Entity
    <Retired>().ToTable("tpt.Retired");
    // TPH mapping
    modelBuilder.Entity<Worker>()
    .Map
    <FreeLancer>(m => m.Requires(f => f.IncCompanyName).HasValue())
    .Map
    <Manager>(m => m.Requires(ma => ma.ManagedEmployeesCount).HasValue())
    .ToTable(
    "tph.Worker");
    }

    这里我使用了一种区分的方法:与默认不同,我要求属于类的列是非空的列。

    使用者不需要与 TPT 区分,甚至在修改了映射之后不会影响使用的代码。

    publicstaticvoid ManageTPH()
    {
    using (var context1 =new HierarchyContext())
    {
    var worker
    =new Worker
    {
    AnnualSalary
    =20000,
    Age
    =25,
    FirstName
    ="Joe",
    LastName
    ="Plumber"
    };
    var freeLancer
    =new FreeLancer
    {
    Age
    =22,
    FirstName
    ="Mike",
    LastName
    ="Smith",
    IncCompanyName
    ="Mike & Mike Inc"
    };
    var manager
    =new Manager
    {
    Age
    =43,
    FirstName
    ="George",
    LastName
    ="Costanza",
    ManagedEmployeesCount
    =12
    };
    // Make sure the tables are empty…
    foreach (var entity in context1.Persons)
    {
    context1.Persons.Remove(entity);
    }
    context1.Persons.Add(worker);
    context1.Persons.Add(freeLancer);
    context1.Persons.Add(manager);

    context1.SaveChanges();
    }
    using (var context2 =new HierarchyContext())
    {
    Console.WriteLine(
    "Persons count: "+ context2.Persons.OfType<PersonBase>().Count());
    Console.WriteLine(
    "Worker: "+ context2.Persons.OfType<Worker>().Count());
    Console.WriteLine(
    "Retired: "+ context2.Persons.OfType<Retired>().Count());
    Console.WriteLine(
    "FreeLancer: "+ context2.Persons.OfType<FreeLancer>().Count());
    Console.WriteLine(
    "Manager: "+ context2.Persons.OfType<Manager>().Count());
    }
    }

    SQL 中的架构如下,这里混合使用了 TPT 和 TPH 。

  • 相关阅读:
    Java静态类
    【Java TCP/IP Socket】深入剖析socket——TCP套接字的生命周期
    【Java TCP/IP Socket】深入剖析socket——TCP通信中由于底层队列填满而造成的死锁问题(含代码)
    【Java TCP/IP Socket】深入剖析socket——数据传输的底层实现
    【Java TCP/IP Socket】基于NIO的TCP通信(含代码)
    【Java TCP/IP Socket】Java NIO Socket VS 标准IO Socket
    【Java TCP/IP Socket】TCP Socket通信中由read返回值造成的的死锁问题(含代码)
    数据结构课后练习题(练习三)7-5 Tree Traversals Again (25 分)
    快速排序详解(lomuto划分快排,hoare划分快排,classic经典快排,dualpivot双轴快排源码)
    Java多线程(一)——线程基础和锁锁锁
  • 原文地址:https://www.cnblogs.com/haogj/p/2040193.html
Copyright © 2011-2022 走看看