翻译的初衷以及为什么选择《Entity Framework 6 Recipes》来学习,请看本系列开篇

2-8 Table per Type Inheritance 建模

问题

  你有这样一张数据库表,它包含一些额外的信息,这些信息来到一张公共的表。你想使用Table per  Type Inheritance(TPT)继承映射建模。

解决方案

  假设你有两张表与一张公共的表密切相关,如图2-17所示,Businiss表与eCommerce表、Retail表有1:0...1关系。最关键的是,eCommerce表和Retail表中有关于Business表中代表业务的额外的信息。(BusinessID).

图2-17 密切相关的表

  表Retail和eCommerce与表Business密切相关,它们包含一些与业务密切相关的属性。 按下面的步骤,使用TPT为这个继承(Retail和eCommerce实体继承自Business实体)建模。

    1、在你的项目中创建一个继承自DbContext的上下文对象EF6RecipesContext;

    2、使用代码清单2-16创建一个POCO实体Business;

      代码清单2-16  创建一个POCO实体Business

     [Table("Business", Schema = "Chapter2")]
public class Business {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int BusinessId { get; protected set; }
public string Name { get; set; }
public string LicenseNumber { get; set; }
}

    3、使用代码清单2-17 创建一个POCO实体eCommerce,它继承至Business类

      代码清单2-17 创建一个POCO实体eCommerce

     [Table("eCommerce", Schema = "Chapter2")]
public class eCommerce : Business {
public string URL { get; set; }
}

    4、使用代码清单2-18 创建一个POCO实体Retail,它继承至Business类

      代码清单2-18 创建一个POCO实体Retail

     [Table("Retail", Schema = "Chapter2")]
public class Retail : Business {
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZIPCode { get; set; }
}

    5.在上下文对象EF6RecipesContext中添加DbSet<Businss>属性; (译注:这里如果不加构造函数,数据将会保存到默认的数据库实例SQLExpress中,导致不少读者认为数据没有保存到数据库表中,构造函数如下代码清单2-18-1所示)

        public Recipe8Context()
: base("name=EF6CodeFirstRecipesContext")
{ }

原理
  表Retail和eCommerce位于,与表Business关系1:0...1的 0...1这一边。这意味着,一个business可以没有额外信息,也可以有额外的Retail和eCommerce信息。在面向对象的编程中,我们有一个基类Business,两个派生类,Retail和eCommerce.

  因为1:0...1的关系,在表Retail和eCommerce中,不可能有在表Business中没有与之相应的行的数据(行)。在面向对象中,派生类继承基类的属性,这是继承的核心。在table per type(常称为TPT)映射中,每个派生类都被表示成一个单独的表。

  代码清单2-19 演示从模型中插入和获取

代码清单2-19 在TPT模型中插入和获取实体

  using (var context = new EF6RecipesContext()) {
var business = new Business {
Name = "Corner Dry Cleaning",
LicenseNumber = "100x1"
};
context.Businesses.Add(business);
var retail = new Retail {
Name = "Shop and Save",
LicenseNumber = "200C",
Address = "101 Main",
City = "Anytown",
State = "TX",
ZIPCode = ""
};
context.Businesses.Add(retail);
var web = new eCommerce {
Name = "BuyNow.com",
LicenseNumber = "300AB",
URL = "www.buynow.com"
};
context.Businesses.Add(web);
context.SaveChanges();
}
using (var context = new EF6RecipesContext()) {
Console.WriteLine("\n--- All Businesses ---");
foreach (var b in context.Businesses) {
Console.WriteLine("{0} (#{1})", b.Name, b.LicenseNumber);
}
Console.WriteLine("\n--- Retail Businesses ---");
foreach (var r in context.Businesses.OfType<Retail>()) {
Console.WriteLine("{0} (#{1})", r.Name, r.LicenseNumber);
Console.WriteLine("{0}", r.Address);
Console.WriteLine("{0}, {1} {2}", r.City, r.State, r.ZIPCode);
}
Console.WriteLine("\n--- eCommerce Businesses ---");
foreach (var e in context.Businesses.OfType<eCommerce>()) {
Console.WriteLine("{0} (#{1})", e.Name, e.LicenseNumber);
Console.WriteLine("Online address is: {0}", e.URL);
}
}

  代码清单2-19 创建并初始化Business实体实例和两个派生类实例。使用上下文中Business实体集中的Add()方法将他们添加到上下文中。

  在查询中,我们迭代上下文中的Businesses实体集访问所有的businesses。对于派生类型,我们使用泛型方法OfType<T>()并通过指定具体的类型参数在实体集中过滤。

代码清单2-19的输出如下:

--- All Businesses ---Corner Dry Cleaning (#100X1)
Shop and Save (#200C)
BuyNow.com(#300AB)
--- Retail Businesses ---Shop and Save (#200C)
101 Main
Anytown, TX 76106
---- eCommerce Businesses ---BuyNow.com(#300AB)
Online address is: www.buynow.com

  TPT是实体框架支持三个继承映射中的一个,另外两个分别是Table per Hierarchy(TPH,将在本章后面部分讨论),和Table per Concrete Type(TPC,见第15章)。

  TPT继承映射提供了数据库方面的灵活性,作为开发人员,我们可以很容易在模型中为新加的表添加派生类型。但是,每个派生类型都会涉及一个额外的join连接,这会降低系统的性能。 在真实的应用中,我们已经看到当派生类型很多时,使用TPT继承映射所带来的性能问题。

  Table per hierarchy(TPH)继承映射,将在2-10小节讲述。它将整个继承类型存储在一张单独的表中,他解决了TPT中的join连接问题,并带来了好的性能。但牺牲了数据库的灵活性。

  Table per concrete(TPC)继承映射,它被实体框架于运行时所支持,但不被设计器支持。关于TPC的重要应用,请见第15章。

2-9 使用条件过滤对象集

问题

  你想在实体类型上创建一个固定的条件来映射表中行的一个子集。

解决方案

  假设你有一张包含账户信息的数据库表,如图2-18所示,该表有一个可为空的列DeletedOn,它保存着账户被删除的日期和时间。如果账户是激活的,列DeletedOn的值为null,你希望实体类型只代表激活的账户,也就是说,是DeletedOn没有值的账户。

图2-18 有DeletedOn列表Account表

  按下面的步骤,为表Account建模,模型中实体类型只代表激活状态的账户

    1、右键你的项目,选择Add(增加) ➤New Item(新建项),然后选择Visual C#条目下的Data模板下的ADO.NET Entity Data Model(ADO.NET实体数据模型)。

    2、选择Generate from database 从一个已存在的数据库创建模型,点击Next(下一步)。

    3、可以选择一个已存在的数据库连接,也可以选择新建一个数据库连接。

    4、在选择数据库窗口,选择表Account。后勾选上确定所生成对象名称的单复数形式、在模型中包含外键列复选框。点击Finish(完成)

    5、单击Account实体并查看映射详细信息窗口,如果映射详细信息窗口未显示。选择工具菜单View(视图) ➤Other Windows(其它窗口) ➤Entity Data Model Mapping Details(实体数据模型映射详细信息)。在映射详细信息窗口中单击 Add a Condition(增加一个条件),然后选择列DeletedOn, 在Operator(操作符)列中,选择Is,在Value/Property(值或属性)列中选择Null. 这样就创建了一个映射条件(如图2-18)

    6、右键DeleltedOn属性并选择Delete,因为我们使用列DeletedOn作为条件映射,我们不把它映射到实体中的属性。 在我们模型中,它的值总是为Null。

原理

  在示例中,我们在实体Account中应用Is Null条件来过滤行中DeletedOn列有值的行。代码清单2-20演示了从Account表插入和获取数据。

代理清单2-20 从Account插入和获取数据

  using (var context = new EF6RecipesContext()) {
context.Database.ExecuteSqlCommand(@"insert into chapter2.account
(DeletedOn,AccountHolderId) values ('2/10/2009',1728)");
var account = new Account { AccountHolderId = };
context.Accounts.Add(account);
account = new Account { AccountHolderId = };
context.Accounts.Add(account);
account = new Account { AccountHolderId = };
context.Accounts.Add(account);
context.SaveChanges();
}
using (var context = new EF6RecipesContext()) {
foreach (var account in context.Accounts) {
Console.WriteLine("Account Id = {0}",
account.AccountHolderId.ToString());
}
}

  代码清单2-20,我们使用传统方式,调用在上下文对象的Database属性的方法ExecuteSqlCommand(),向数据库中插入一行数据。之所有这样做,是因为我们需要插入一行DeletedOn列为非null值的数据。在模型中,Account实体没有属性映射此列。实际上,Account实体已不可能被映射到一个有DeletedOn列值的行,而这又恰好是我们需要用来测试的。

  第一部分余下的代码,我们创建并初始化了3个Account实体类型的实例,并通过SaveChanges()方法保存到数据加中。

  当我们查询数数据库时,我们只能获取到3个通过SaveChanges()方法保存到数据库中的Account实体类型实例。通过方法ExecuteSqlCommand()方法插入的数据将不被显示。下面的输出验证了这个结论:

Account Id = 2320
Account Id = 2502
Account Id = 2603

这篇就到此结束,下篇我们开始TPH.

实体框架交流QQ群:  458326058,欢迎有兴趣的朋友加入一起交流

谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/

《Entity Framework 6 Recipes》中文翻译系列 (8) -----第二章 实体数据建模基础之继承关系映射TPT的更多相关文章

  1. 《Entity Framework 6 Recipes》中文翻译系列 (9) -----第二章 实体数据建模基础之继承关系映射TPH

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-10 Table per Hierarchy Inheritance 建模 问题 ...

  2. 《Entity Framework 6 Recipes》翻译系列 (3) -----第二章 实体数据建模基础之创建一个简单的模型

    第二章 实体数据建模基础 很有可能,你才开始探索实体框架,你可能会问“我们怎么开始?”,如果你真是这样的话,那么本章就是一个很好的开始.如果不是,你已经建模,并在实体分裂和继承方面感觉良好,那么你可以 ...

  3. 《Entity Framework 6 Recipes》翻译系列 (4) -----第二章 实体数据建模基础之从已存在的数据库创建模型

    不知道对EF感兴趣的并不多,还是我翻译有问题(如果是,恳请你指正),通过前几篇的反馈,阅读这个系列的人不多.不要这事到最后成了吃不讨好的事就麻烦了,废话就到这里,直奔主题. 2-2 从已存在的数据库创 ...

  4. 《Entity Framework 6 Recipes》翻译系列 (5) -----第二章 实体数据建模基础之有载荷和无载荷的多对多关系建模

    2-3 无载荷(with NO Payload)的多对多关系建模 问题 在数据库中,存在通过一张链接表来关联两张表的情况.链接表仅包含连接两张表形成多对多关系的外键,你需要把这两张多对多关系的表导入到 ...

  5. 《Entity Framework 6 Recipes》中文翻译系列 (10) -----第二章 实体数据建模基础之两实体间Is-a和Has-a关系建模、嵌入值映射

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-11 两实体间Is-a和Has-a关系建模 问题 你有两张有Is-a和Has-a ...

  6. 《Entity Framework 6 Recipes》中文翻译系列 (7) -----第二章 实体数据建模基础之拆分实体到多表以及拆分表到多实体

    2-6 拆分实体到多表 问题 你有两张或是更多的表,他们共享一样的主键,你想将他们映射到一个单独的实体. 解决方案 让我们用图2-15所示的两张表来演示这种情况. 图 2-15,两张表,Prodeuc ...

  7. 《Entity Framework 6 Recipes》中文翻译系列 (6) -----第二章 实体数据建模基础之使用Code First建模自引用关系

    2-5 使用Code First建模自引用关系 问题 你的数据库中一张自引用的表,你想使用Code First 将其建模成一个包含自关联的实体. 解决方案 我们假设你有如图2-14所示的数据库关系图的 ...

  8. 《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述

    微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF ...

  9. 《Entity Framework 6 Recipes》翻译系列(2) -----第一章 开始使用实体框架之使用介绍

    Visual Studio 我们在Windows平台上开发应用程序使用的工具主要是Visual Studio.这个集成开发环境已经演化了很多年,从一个简单的C++编辑器和编译器到一个高度集成.支持软件 ...

随机推荐

  1. Mac Android开发环境变量的配置(java、sdk、ndk、gradle)

    1.打开terminal 2.然后输入 vi .bash_profile 后按"e"进入编辑模式 3.输入想要配置的环境变量(Java.sdk.ndk.gradle): expor ...

  2. Linux usual cmd

    日常工作时常需要用到,在此备份一下: <1> top命令 第一行:当前系统时间为23:31:59,系统已经运行了127天又19小时47分钟,当前系统只要一个用户即root,load ave ...

  3. URAL 2089 Experienced coach Twosat

    Description Misha trains several ACM teams at the university. He is an experienced coach, and he doe ...

  4. 我理想中的父母(The Ideal Parents In My Heart)

    Parents are the first teachers in children's life, and people all know the great importance exactly ...

  5. 【统计学习】SVM之超平面方程来源

    摘要 本文主要说明SVM中用到的超平面方程是怎么来的,以及各个符号的物理意义,怎么算空间上某点到该平面的距离. 正文 < 统计学习方法>一书给出如下说明: 首先说明我对超平面的理解: 在三 ...

  6. XVII Open Cup named after E.V. Pankratiev. GP of SPb

    A. Array Factory 将下标按前缀和排序,然后双指针,维护最大的右边界即可. #include<cstdio> #include<algorithm> using ...

  7. c、c++ 常用函数记录

    1.void*  memcpy(char*dest, char*src, size_tnum) 将第二个参数拷贝到第一个参数,最后一个是长度. 2.void *memset(void *s, int ...

  8. [机器学习] 深度学习之caffe1——软件配置与测试

    caffe的编译配置真的是很让人头疼啊,不知道试过多少次了~~~ 重装系统了七八次,搞得linux的一些常用命令倒是很熟悉了~~~ 我有洁癖~~~某一个点上出了错,我一定要把它搞好了,再重新来一次,我 ...

  9. js中使用new Date(str)创建时间对象不兼容firefox和ie的解决方式

    /** * 解决 ie,火狐浏览器不兼容new Date(s) * @param strDate * 返回 date对象 * add by zyf at 2015年11月5日 */ function ...

  10. BZOJ4596: [Shoi2016]黑暗前的幻想乡

    Description 四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖 怪涌入了幻想乡,扰乱了幻想乡昔日的秩序.但是幻想乡的建制派妖怪(人类) 博丽灵梦和八云紫等人整日高谈所有妖怪 ...