本篇日记我们将详细探讨如何将表现领域的类映射到现有的数据库。现在的经济形势不是太好,很多公司都取消了开发新系统的预算。在这种情况下,通常的做法是把原有的几个系统修改一下做个集成,先凑合用着得了。如果要对原有的系统做功能提升的话,肯定要重用原来的数据库结构并做一些小的改进,在这种情况下我们怎样使用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. 第二百零二天 how can I 坚持

    最近增肥好明显,胃口好没办法,只要肚子起不来就行了.加油. 其实挺幸福,想吃啥吃啥. 鱼会不会被冻死,买了加热棒不想用,该咋办呢. 股市又跌没了一千多,还是不够睿智,不够淡定. 人活这一辈子,到底最想 ...

  2. Spark RDD概念学习系列之RDD的创建(六)

    RDD的创建  两种方式来创建RDD: 1)由一个已经存在的Scala集合创建 2)由外部存储系统的数据集创建,包括本地文件系统,还有所有Hadoop支持的数据集,比如HDFS.Cassandra.H ...

  3. 【多线程同步案例】Race Condition引起的性能问题

    Race Condition(也叫做资源竞争),是多线程编程中比较头疼的问题.特别是Java多线程模型当中,经常会因为多个线程同时访问相同的共享数据,而造成数据的不一致性.为了解决这个问题,通常来说需 ...

  4. <系统函数实现>memcmp

    这是我实现的memcmp函数: #include <stdio.h> #include <string.h> /* *int memcmp (const void *s1,co ...

  5. Java并发编程:Timer和TimerTask

    Java并发编程:Timer和TimerTask 下面内容转载自: http://blog.csdn.net/xieyuooo/article/details/8607220 其实就Timer来讲就是 ...

  6. HDU 5862 Counting Intersections (树状数组)

    Counting Intersections 题目链接: http://acm.split.hdu.edu.cn/showproblem.php?pid=5862 Description Given ...

  7. UVaLive 7500 Boxes and Balls (数学)

    题意:给定 n 个球,每次从每篮子里拿出来一个放在一个新篮子里,并移除相同的,按球的个数进行排序,问你用最多几个球能完成循环. 析:数学问题,很容易发现前n项和就是最多的球数,所以我们只要找最大的n项 ...

  8. ags js下载地址

    https://developers.arcgis.com/en/downloads/ 备用

  9. 【JUnit】EasyMock用法总结

    使用EasyMock的总体步骤 1.生成Mock接口 IService mockService = EasyMock.createMock("name", IService.cla ...

  10. Hibernate 与 Spring 的整合

    刚刚学习了hibernate和Spring的整合,现在来总结一下. 以实现一个功能为例,与大家分享一下整个过程. 需要实现的功能:建立一个Person类,该类包括name,sex,age,birtha ...