本篇日记我们将详细探讨如何将表现领域的类映射到现有的数据库。现在的经济形势不是太好,很多公司都取消了开发新系统的预算。在这种情况下,通常的做法是把原有的几个系统修改一下做个集成,先凑合用着得了。如果要对原有的系统做功能提升的话,肯定要重用原来的数据库结构并做一些小的改进,在这种情况下我们怎样使用Code First呢?我们可以按照原有系统的业务逻辑和CR(Change Request)中的新业务逻辑建立domain中的类,然后使用Fluent API把这些类映射到原有数据库的表中,或修改原有系统的数据结构。我相信通过前几篇的日记,大家已经知道了怎样去自定义各个方面的数据库映射了。这种方法虽然给我们机会重新审视我们的业务逻辑,但是原有系统中许多没有变化的部分也需要按照Code First的方法重头创建类并映射到原有的数据表,给我们的开发带来了很多不必要的工作量。为了提高我们的开发效率,减少在使用Code First升级系统时不必要的工作量,微软发明了Entity Framework Power Tools. 这个工具可以使用反向工程,在原有数据库的基础上生成Code First中使用的纯粹代表domain的类和数据库映射配置。博客园上有很多关于这个工具的介绍,大家可以上博客园搜索。这个工具虽然很好,但是他只能帮你减少你的工作量,不可能完全替代你的工作。比如说原来的数据库中有一个表,你经过对业务逻辑的分析,发现表中的字段应该属于两个类,但是你还不能改变现有的数据库结构,这种情况需要你自己配置数据库映射来搞定。或者原来数据库中的两个表,在你的新的业务逻辑中属于同一个类,也给根据特定的业务逻辑进行数据库映射的配置。所以我今天主要介绍如何解决以上的两个问题。

1.在不修改数据库结构的前提下,如何将两个类映射到同一个数据库表。

2.在不修改数据库结构的前提下,如何将一个类映射到两个数据库表。

1.在不修改数据库结构的前提下,如何将两个类映射到同一个数据库表。

我们已经介绍过一种将两个类映射成一个表的方法:ComplexType,今天我们将介绍另外一种方法。

假设我们的原有数据库中有一个Customer表,结构如下:

我们在对现有系统进行升级改造的时候,使用了Code First。请大家注意,这个表中我用红色方框标识出来列,从业务逻辑上来说,并不是客户信息的一部分,而是客户的银行账号信息。按照业务逻辑中的实际情况,我们建立了两个类:Customer和BankAccount。

Customer类:

public class Customer
{
public string IDCardNumber { get; set; }
public string CustomerName { get; set; }
public string Gender { get; set; }
public Address Address { get; set; }
public string PhoneNumber { get; set; }
public BankAccount Account { get; set; }
}

BankAccount类:

public class BankAccount
{
public string AccountNumber { get; set; }
public DateTime CreatedDate { get; set; }
public string BankName { get; set; }
public string AccountName { get; set; }
}

如果我们需要让Code First把这两个类映射到同一个表,这两个还必须满足两个条件:

1.两个类必须共享同一个主键。

2.两个类之间的关系必须被映射为表之间的一对一关系。

为了满足这两个条件,我们首先需要修改BankAccount类,让BankAccount类使用与Customer类同样的主键。

public class BankAccount
{
public string IDCardNumber { get; set; }
public string AccountNumber { get; set; }
public DateTime CreatedDate { get; set; }
public string BankName { get; set; }
public string AccountName { get; set; }
}

我们还必须在Customer的数据库配置类中手动地映射这两个类之间的一对一关系。

HasRequired(c => c.Account).WithRequiredDependent();

满足了以上的两个条件之后我们还必须使用ToTable方法手动地把这两个映射到同一个表。

public class CustomerEntityConfiguration:EntityTypeConfiguration<Customer>
{
public CustomerEntityConfiguration()
{
ToTable("Customers");
HasKey(c => c.IDCardNumber).Property(c => c.IDCardNumber).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None).HasMaxLength(50);
Property(c => c.IDCardNumber).HasMaxLength(20);
Property(c => c.CustomerName).IsRequired().HasMaxLength(50);
Property(c => c.Gender).IsRequired().HasMaxLength(1);
Property(c => c.PhoneNumber).HasMaxLength(20);
HasRequired(c => c.Account).WithRequiredDependent();
}
}

public class BankAccountValueObjectConfiguration: EntityTypeConfiguration<BankAccount>
{
public BankAccountValueObjectConfiguration()
{
ToTable("Customers");
HasKey(ba => ba.IDCardNumber).Property(ba => ba.IDCardNumber).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None).HasMaxLength(20);
Property(ba => ba.AccountNumber).IsRequired().HasMaxLength(50);
Property(ba => ba.AccountName).IsRequired().HasMaxLength(100);
Property(ba => ba.BankName).IsRequired().HasMaxLength(100);
}
}

我们修改一下以前使用的单元测试方法来测试Code First是否把这两个类映射到同一个表:

 [TestMethod]
public void CanAddCustomerWithBankAccount()
{
OrderSystemContext unitOfWork = new OrderSystemContext();
CustomerRepository repository = new CustomerRepository(unitOfWork);
Customer newCustomer = new Customer() { IDCardNumber = "120104198106072518", CustomerName = "Alex", Gender = "M", PhoneNumber = "test" };
Address customerAddress = new Address { Country = "China", Province = "Tianjin", City = "Tianjin", StreetAddress = "Crown Plaza", ZipCode = "300308" };
BankAccount account = new BankAccount { IDCardNumber = "120104198106072518", AccountNumber = "2012001001", BankName = "ICBC", AccountName = "Alex", CreatedDate = DateTime.Parse("2012-1-21") };
newCustomer.Address = customerAddress;
newCustomer.Account = account;
repository.AddNewCustomer(newCustomer);
unitOfWork.CommitChanges();
}

执行完测试方法之后,我们打开数据库看一下Code First映射出的数据库结构是否与我们原来的数据库结构一样。

通过手动地将这两个类映射成同一个表,使我们的代码可以再不修改原有数据库结构的基础上根据业务逻辑重构我们的代码。

2.在不修改数据库结构的前提下,如何将一个类映射到两个数据库表

假设我们原有的数据库中有两个表,一个是ProductCatalog,另一个是ProductCatalogPhoto。ProductCatalog表存储某类产品的信息,但是后来加新需求的时候,需要显示该类产品的照片和对照片的描述。不知道是哪位仁兄不敢动ProductCatalog表,直接加了一个ProductCatalogPhoto表。现在轮到我们升级系统的时候,就会面临下面这样的数据库结构:

但是傻子都能看出来,从ProductCatalog和ProductCatalogPhoto两个表创建出两个类肯定是不符合业务逻辑的。我们就需要创建ProductCatalog类,它应该既包括某类产品的信息,也应该包括该类产品的照片和对照片的描述。

public class ProductCatalog
{
public int ProductCatalogId { get; set; }
public string CatalogName { get; set; }
public string Manufactory { get; set; }
public decimal ListPrice { get; set; }
public decimal NetPrice { get; set; }
public List<Product> ProductInStock { get; set; }
public List<SalesPromotion> SalesPromotionHistory { get; set; }
public byte[] Photo { get; set; }
public string PhotoDescription { get; set; } .............
}

在不改变原来数据库结构的情况下,我们就需要通过手动配置将ProductCatalog类映射到ProductCatalog和ProductCatalogPhoto两个表。

public class ProductCatalogEntityConfiguration:EntityTypeConfiguration<ProductCatalog>
{
public ProductCatalogEntityConfiguration()
{
this.Property(c => c.CatalogName).HasMaxLength(200).IsRequired();
this.Property(c => c.Manufactory).HasMaxLength(200);
this.Property(c => c.ListPrice).HasPrecision(18, 4);
this.Property(c => c.NetPrice).HasPrecision(18, 4);
this.HasKey(c => c.ProductCatalogId);
Map(c =>
{
c.Properties(p => new {p.CatalogName,p.Manufactory,p.ListPrice,p.NetPrice});
c.ToTable("ProductCatalog");
});
Map(c =>
{
c.Properties(p => new {p.Photo,p.PhotoDescription});
c.ToTable("ProductCatalogPhoto");
}); }
}

我们通过Map方法,首先选择类的属性,然后将选择的属性列表映射到某个数据库表。

我们在我们的测试程序里新加一个单元测试,测试一下,Entity Framework Code First是不是将这个类实例持久化到两个表中。

        [TestMethod]
public void CanAddNewProductCatalogWithPhoto()
{
OrderSystemContext unitOfWork = new OrderSystemContext();
ProductRepository repository = new ProductRepository(unitOfWork);
ProductCatalog catalog = new ProductCatalog() { CatalogName = "DELL Laptop E6400", Manufactory = "DELL", ListPrice = 6000, NetPrice = 5000, Photo = new byte[] { 0 }, PhotoDescription = "E6400" };
repository.AddNewProductCatalog(catalog);
unitOfWork.CommitChanges();
}

执行完这个单元测试程序之后,我们可以到数据库中查询一下,看看是不是我们想要的结果:

大家可以看到,Entity Framework Code First已经将ProductCatalog 类的实例持久化到ProductCatalog和ProductCatalogPhoto两个表中。

EF——一个实体对应两张表,两个实体对应一张表 06 (转)的更多相关文章

  1. EF Core中如何正确地设置两张表之间的关联关系

    数据库 假设现在我们在SQL Server数据库中有下面两张表: Person表,代表的是一个人: CREATE TABLE [dbo].[Person]( ,) NOT NULL, ) NULL, ...

  2. MySQL实验1: 新建一个名为 library 的数据库,包含 book、reader 两张表,根据自己的理解安排表的内容并插入数据。

    数据表(table)简称表,它是数据库最重要的组成部分之一.数据库只是一个框架,表才是实质内容. 实验: 新建一个名为 library的数据库,包含 book.reader两张表,根据自己的理解安排表 ...

  3. sql 查询 一张表里面的数据 在另一张表中是否存在 和 比对两个集合中的差集和交集(原创)

    这两天在搞一个修复的小功能 需求: A表,B表,C表,日志文件 先筛选出A表和B表中都符合条件的数据,然后检查这些数据在C表中是否存在.如果不存在,就从日志中读取数据,存入C表中,如果存在,则不做操作 ...

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

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

  5. 表单验证:$tablePrefix(定义表前缀);$trueTableName = 'yonghu',找到真实表名(yonghu)表;create($attr,0)两个参数;批量验证(返回数组);ajax+动态验证表单

    *$tablePrefix是定义在Model中的,优先级大于配置文件中,如果项目中表前缀全部比如为"a_",并且在配置文件中定义了 'DB_PREFIX'=>'a_' 后期如 ...

  6. sql server编写通用脚本自动检查两个不同服务器的新旧数据库的表结构差异

    问题:工作过程中,不管是什么项目,伴随着项目不断升级版本,对应的项目数据库业务版本也不断升级,数据库出现新增表.修改表.删除表.新增字段.修改字段.删除字段等变化,如果人工检查,数据库表和字段比较多的 ...

  7. [转帖]删除一张大表时为什么undo占用空间接近原表两倍?

    删除一张大表时为什么undo占用空间接近原表两倍? https://www.toutiao.com/i6736735016492990983/ 原创 波波说运维 2019-09-22 00:01:00 ...

  8. 剑指offer40:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字

    1 题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 2 思路和方法 (1)异或:除了有两个数字只出现了一次,其他数字都出现了两次.异或运算中,任 ...

  9. LeetCode2-链表两数和

    目录 LeetCode2-链表两数和 题目描述 示例提示 经验教训 参考正解 题目描述 示例提示 经验教训 链表题的判空条件不是万能的,有时候示例会极其复杂,根本难以通过判空来区分不同情况. /** ...

  10. sql把一个表数据插入到另一张表

    把一个表数据插入到另一张表 insert into tableB (field1,field2,field3,field4) select field1,field2,field3,'val4' fr ...

随机推荐

  1. Struts – Wildcards example

    Struts wildcards can helps to reduce the repetition in your struts-config.xml file, as long as your ...

  2. 关于 三星 I9100 (水货)

    前天陪好友去买水货9100,总结了一点经验,觉得挺有用的,今天整理一下写出来...有 需要的可以看看..原创整理.. 一,当然是检查外观(检查USB接口有没有磨损,检查摄像头是否有灰尘,检查屏幕是不是 ...

  3. 实时监控MySql状态

    大多网站的性能瓶颈都会出在数据库上,所以想把Mysql监控起来,就搜索了下相关资料. 后来和同事讨论了下cacti和nagios有些老套和过时,graphite比较时尚,然后就搜了下相关的资料,最后搞 ...

  4. 深入DNS

    什么是DNS? 我说前面说过http如何发送请求.这里的第一步就是将域名变为ip地址 如何将域名变为ip地址我们就得用到域名解析(DNS). 如何进行域名解析的? 第一步:在浏览器的url里输入域名, ...

  5. Android导入自定义的jar包时出现 E/AndroidRuntime(486): java.lang.NoClassDefFoundError错误

    把自定义的jar包放在Android的工程的libs目录下,运行程序,会出现一下错误: 10-10 08:34:06.479: E/dalvikvm(486): Could not find clas ...

  6. 《dive into python3》 笔记摘录

    1.list can hold  arbitrary  objects and can expand dynamically as new items are added. A list is an  ...

  7. div+css样式表的id,class的常用命名规则

    div+css样式表的id的常用命名规则如下表所示: div+css样式表的id的常用命名规则如下表所示: 页头 header 登录条 loginBar 标志 logo 侧栏 sideBar 广告 B ...

  8. C++开发必看 四种强制类型转换的总结 [转]

    一.C风格的强制类型转换(Type Cast)很简单,不管什么类型的转换统统是:     TYPE b = (TYPE)a 二.C++风格的类型转换提供了4种类型转换操作符来应对不同场合的应用. co ...

  9. (剑指Offer)面试题34:丑数

    题目: 把只包含因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含因子7. 习惯上我们把1当做是第一个丑数.求按从小到大的顺序的第N个丑数. 思路: 1. ...

  10. 初始化css代码需要注意的

    (从已经死了一次又一次终于挂掉的百度空间人工抢救出来的,发表日期 2014-05-06) 写在所有css代码之前,对网页中所有同类元素的一个样式规则代码或者一些基础性公用元素的样式规则代码. 1.空白 ...