CodeFirst与EntityFramework【续】
在介绍一对多关系和多对多关系时,大家应该已经注意到了只要存在依赖关系的两个类的定义中包含对方的实例或实例的集合,Entity Framework Code First会自动推断出与之对应的数据库关系。这个方式对一对一关系也同样适用吗?先让我们来作一个实验。
假设我们的订单系统现在需要存储每个客户的银行账号信息。显然,在我们的订单系统中,银行账号并不是我们关注的重点,我只需要保存账号的号码,开户行以及账号名称,由此可见银行账号在我们这里只是一个值对象(Value Object)。
我们需要定义银行账号类:
public class BankAccount
{
public string AccountNumber { get; set; }
public DateTime CreatedDate { get; set; }
public string BankName { get; set; }
public string AccountName { get; set; }
}
接着,我们还需要在客户类当中包含一个银行账号类的实例:
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; }
}
我们写一个单元测试程序,看看Entity Framework 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 { AccountNumber = "2012001001", BankName = "ICBC",
AccountName = "Alex", CreatedDate = DateTime.Parse("2012-1-21") };
newCustomer.Address = customerAddress;
newCustomer.Account = account;
repository.AddNewCustomer(newCustomer);
unitOfWork.CommitChanges();
}
我们运行一下我们的单元测试程序,程序会抛出异常:
Test method EntityFramework.CodeFirst.Demo1.UnitTest.CustomerRepositoryUnitTest.CanAddCustomerWithBankAccount threw exception:
System.Data.DataException:
An exception occurred while initializing the database. See the
InnerException for details. --->
System.Data.Entity.Infrastructure.DbUpdateException: Null value for
non-nullable member. Member: 'Account'. --->
System.Data.UpdateException: Null value for non-nullable member. Member:
'Account'.
这是为什么呢?因为Entity Framework Code First无法根据类之间的依赖关系推断并建立一对一关系,它根本搞不清楚在这两个存在依赖关系的类中,哪个是主表,哪个是子表,外键应该建立在哪个表中。一对多关系中非常容易分清主表和子表,哪个类中包含另一个的实例集合,它就是主表。多对多关系是通过连接表建立的,不需要分清主表和子表。但是到一对一关系时,这就是个问题了。
要想让Entity Framework Code First根据类之间的依赖关系推断并建立一对一关系,你必须帮助它,告诉他哪个是主表,哪个是子表。
这里做个备注:关于值对象(Value Object):
什么情况下用值对象?
i、定义的Model类中字段都是基本类型;
ii、所谓的值对象就是一些没有生命周期,也没有业务逻辑上唯一标识符的类(也就是说无关紧要、可有可无的Model类)。
iii、哪些类是Entity,哪些类是Value Object不是固定的,取决于具体的业务逻辑。
假设一个银行账号必须有对应的客户,但是客户可以没有银行账号,并且由于银行账号是个值对象,没有必要让它包含客户类的实例。
因为银行账号类的定义中并不包含客户类的实例,所以我们需要在客户类的配置方法中设定这个一对一关系。
public class CustomerEntityConfiguration:EntityTypeConfiguration<Customer>
{
public CustomerEntityConfiguration()
{
HasKey(c => c.IDCardNumber).Property(c =>
c.IDCardNumber).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(c => c.IDCardNumber).HasMaxLength(20);
this.Property(c => c.CustomerName).IsRequired().HasMaxLength(50);
this.Property(c => c.Gender).IsRequired().HasMaxLength(1);
this.Property(c => c.PhoneNumber).HasMaxLength(20);
this.HasOptional(c => c.Account).WithOptionalDependent();
}
}
在客户类中的HasOptional意味着客户类可以有也可以没有银行账号。当我们通过HasOptional指定Customer与BankAccount类的关系时,Entity
Framework Code
First要求我们指定它们之间的依赖关系。这时,IntelliSense会让你在两个方法之间进行选择:WithOptionalDependent和WithOptionalPrincipal。如果你选择WithOptionalDependent则代表Customer表中有一个外键指向BankAccount表的主键,如果你选择WithOptionalPrincipal则相反,BankAccount拥有指向Customer表的外键。
执行一下我们的单元测试,这次就不会报错了。然后我们打开SQL Server,我们发现Entity Framework Code First映射到正确的一对一关系。
如图:
大家可以看到Customer表中的外键是可以为空的,这是由于我们使用了HasOptional。如果我们需要一对一关系中的外键不能为空,我们就需要使用HasRequired.
HasRequired(c => c.Account).WithRequiredDependent();
当我们使用HasRequired时候,IntelliSense会让你在WithRequiredDependent和WithRequiredPrinciple之间选择, 这里的dependent和principle也是用于决定主键在哪个表的。
Code First处理类之间的继承关系
1.Table Per Hierarchy(TPH): 只建立一个表,把基类和子类中的所有属性都映射为表中的列。
Code First默认会把基类和子类的所有属性都映射成一个表中的列,并且会增加一个Discriminator列标识存进去的是哪个类的实例。
如:
2.Table Per Type(TPT): 为基类和每个子类建立一个表,每个与子类对应的表中只包含子类特有的属性对应的列。
在这种处理方式中,Entity Framework Code First会为每个基类和子类建立一个表,子类的表中只包含子类特有的属性。
如:
3.Table Per Concrete Type(TPC):为每个子类建立一个表,每个与子类对应的表中包含基类的属性对应的列和子类特有属性对应的列。
Code First默认使用的是TPC方式。
在这种处理方式中,Entity Framework Code First为每一个子类建立一个表,在子类对应的表中除了子类特有的属性外还有基类的属性对应的表。
和TPT一样,我们也需要通过Map方法进行设置。
public class SalesPersonValueObjectConfiguration: EntityTypeConfiguration<SalesPerson>
{
public SalesPersonValueObjectConfiguration()
{
HasKey(p => p.EmployeeID).Property(p => p.EmployeeID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Property(p => p.Name).IsRequired().HasMaxLength(100);
Property(p => p.Gender).IsRequired().HasMaxLength(1);
Map<SalesMan>(salesman => { salesman.ToTable("SalesMan"); salesman.MapInheritedProperties(); });
Map<SalesManager>(manager => { manager.ToTable("Manager"); manager.MapInheritedProperties(); });
}
}
通过MapInheritedProperties方法就可以强制Code First使用TPC方式。
我们重新编译之后执行我们原来的测试方法,可以得到不同的数据表结构,Code First不会为基类建立表,而是为每个子类都建立一个表,将子类的内容和基类的内容都存储到各个子类对应的表中。
如:
PS:如果你的基类是abstract,效果也是一样的。
最后需要探讨的一个问题是我们在实际项目中应该使用哪种方式呢?
1.不推荐使用TPC(Type
Per Concrete
Type),因为在TPC方式中子类中包含的其他类的实例或实例集合不能被映射为表之间的关系。你必须通过手动地在类中添加依赖类的主键属性,从而让Code
First感知到它们之间的关系,而这种方式是和使用Code First的初衷相反的。
2.从查询性能上来说,TPH会好一些,因为所有的数据都存在一个表中,不需要在数据查询时使用join。
3.从存储空间上来说,TPT会好一些,因为使用TPH时所有的列都在一个表中,而表中的记录不可能使用所有的列,于是有很多列的值是null,浪费了很多存储空间。
4.从数据验证的角度来说,TPT好一些,因为TPH中很多子类属性对应的列是可为空的,就为数据验证增加了复杂性。
所以说具体的项目中选择哪种方式取决于你的实际项目需要。
本文内容来源:http://www.cnblogs.com/lk8167/archive/2013/01/22/2871011.html
EF Code First弊端(自己总结):
虽然微软从EF4.1版本开始支持“Code First”这种编程方式,但是这种方式存在以下弊端:
1、配置数据表关联关系复杂。虽然用Fluent API可以动态设置数据表之间的关联关系,如主键、外键、一对多、多对多等,但是设置代码相对复杂,如果代码层次设计不好的话很难读懂和维护;
2、数据迁移麻烦;
3、项目改造时,与现有系统整合性差;
CodeFirst与EntityFramework【续】的更多相关文章
- CodeFirst与EntityFramework
项目添加EntityFramework命令:Install-Package EntityFramework CodeFirst默认规则1. 数据库映射:Code First 默认会在本地的SQL Ex ...
- CodeFirst 的编程方式
第一步:创建控制台项目第二步:添加新建项目→Ado.Net空实体模型第三步:添加实体:Customer,添加几个必要的测试字段第四步:添加实体之间的联系第五步:根据模型生成数据库脚本,并执行sql脚本 ...
- CodeFirst命令
CodeFirst get-help entityFramework NuGet命令 Add-Migration Adds a new mig ...
- Entity Framework 5.0系列之Code First数据库迁移
我们知道无论是"Database First"还是"Model First"当模型发生改变了都可以通过Visual Studio设计视图进行更新,那么对于Cod ...
- [1] Entity Framework / Code First
CodeFirst是EntityFramework的一种技术手段,因为传统编程方式都是先建立数据库,然后根据数据库模型为应用程序建模,再进行开发:CodeFirst从字面上理解就是代码先行,先在程序中 ...
- 第三篇:Entity Framework CodeFirst & Model 映射 续篇 EntityFramework Power Tools 工具使用
上一篇 第二篇:Entity Framework CodeFirst & Model 映射 主要介绍以Fluent API来实作EntityFramework CodeFirst,得到了大家一 ...
- EntityFramework CodeFirst SQLServer转Oracle踩坑笔记
接着在Oracle中使用Entity Framework 6 CodeFirst这篇博文,正在将项目从SQLServer 2012转至Oracle 11g,目前为止遇到的问题在此记录下. SQL Se ...
- EntityFramework系列:SQLite.CodeFirst自动生成数据库
http://www.cnblogs.com/easygame/p/4447457.html 在Code First模式下使用SQLite一直存在不能自动生成数据库的问题,使用SQL Server C ...
- EntityFramework 5.0 CodeFirst 教程04-查询,插入,更新,和删除数据
---------------------目录-------------------------- EntityFramework 5.0 CodeFirst 教程04-查询,插入,更新,和删除数据 ...
随机推荐
- 以太坊 Geth 环境搭建(Ubuntu)
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u014409380/article/details/79897335 时隔多日,我又想起来更新博客, ...
- ubuntu mysql 的安装、配置、简单使用,navicat 连接
MySQL 的安装 1. 先更新 apt 安装中心: apt update 里面会有默认最新的mysql 的包. 2.安装msyql : sudo apt-get install mysql-serv ...
- Session_start的使用
PHP session用法其实很简单它可以把用户提交的数据以全局变量形式保存在一个session中并且会生成一个唯一的session_id,这样就是为了多了不会产生混乱了,并且session中同一浏览 ...
- centos7用yum安装node.js v8.x
1.更新node.js v8.x yum源 // 不更新,提示没有 packages文件 # curl --silent --location https://rpm.nodesource.com/s ...
- Zookeeper 记录
本文主要是学习记录: 部分内容为 <从Paxos到Zookeeper> 部分内容为 zookpper 原理分析 https://www.cnblogs.com/leesf456/p/ ...
- vue-cli 3.x搭建项目以及其中vue.config.js文件的配置
参考链接:https://www.cnblogs.com/wxh0929/p/11271551.html vue-cli3全面配置详解:https://www.jb51.net/article/150 ...
- vue组件库element-ui 的Table内容显示不更新
一.问题原因: 因为数组直接赋值不能被 Object.defineProperty 检测到. 二.解决方法 所以应该要使用this.$set(‘对象名’,要修改的属性名,属性值),这样新添加的属性值 ...
- zotero入门简介
文献管理工具必备的功能:word文档中插入文献引用,自动生成参考文献列表. 支持系统:windows, linux, macOS. 费用:免费提供300M以内文献库存储容量. 其他:支持笔记,条目附件 ...
- 亿级Web系统搭建――单机到分布式集群 转载
当一个Web系统从日访问量10万逐步增长到1000万,甚至超过1亿的过程中,Web系统承受的压力会越来越大,在这个过程中,我们会遇到很多的问题.为了解决这些性能压力带来问题,我们需要在Web系统架构层 ...
- 【转帖】sysbench的安装和做性能测试
iMySQL | 老叶茶馆 sysbench的安装和做性能测试 http://imysql.cn/node/312 我仿照这个学的 但是 需要用更新的版本才可以. By yejr on 14 六月 ...