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-查询,插入,更新,和删除数据 ...
随机推荐
- Flink简介
Flink简介 Flink的核心是一个流式的数据流执行引擎,其针对数据流的分布式计算提供了数据分布,数据通信以及容错机制等功能.基于流执行引擎,Flink提供了诸多更高抽象层的API以方便用户编写分布 ...
- python 学生表
1,主页面函数(01-mainpage.py) import json import file_manager import student_system ''' ''' # 全局变量 file_na ...
- 19.网络插件calico
19.网络插件calico 官网: https://docs.projectcalico.org/v3.8/introduction/ calico默认工作在192.168.0.0/16 的网络 ca ...
- pymysql检查是否断开, 断开重连
python mysql使用持久链接 python链接mysql中没有长链接的概念,但我们可以利用mysql的ping机制,来实现长链接功能~ 思路: 1 python mysql 的cping 函数 ...
- python定义接口继承类invalid syntax解决办法
class s_all(metaclass=abc.ABCMeta): #python2.7用此方法定义接口继承 # __metaclass__ = abc.ABCMeta @abc.abstract ...
- Oracle查询表和字段
查看表字段.类型.注释 SELECT A.COLUMN_NAME,B.comments,A.DATA_TYPE FROM USER_TAB_COLUMNS A LEFT JOIN user_col_c ...
- Allegro静态铜皮避让问题
使用Allegro的人都知道,Allegro的铜分为静态和动态,我的设计习惯是需要满足载流地方一般使用静态铜皮,避免设计过程中因为打孔把铜皮割裂,这是静态铜皮的一个特性,不会自动避让,强制打孔或者走线 ...
- 常用的linux命令选项
-a 显示所有对象 -c 生成一个计数 -d 制定一个目录 -e 扩展一个对象 -f 指定读入数据的文件 -h 显示命令的帮助信息 -i 忽略文本大小写 -l 产生输出的成格式版本 -n 使用非交互模 ...
- linux基本防护措施,权限分配和提高防护安全
设置用户失效 1.失效的用户将无法登录 使用chage命令将用户zhangsan的账户设为当前已失效(比如已经过去的某个时间): [root@proxy ~]# useradd zhangsan [r ...
- [转载]由浅入深探究mysql索引结构原理、性能分析与优化
第一部分:基础知识第二部分:MYISAM和INNODB索引结构1. 简单介绍B-tree B+ tree树 2. MyisAM索引结构 3. Annode索引结构 4. MyisAM索引与InnoDB ...