Entity Framwork CodeFirst 学习笔记三:使用复杂类型
上一篇,主要学习了一些CodeFirst 中修改默认规约的基本配置。在文章最后,出现了个新的名词:复杂类型。什么是复杂类型呢?
书中说道:“复杂类型也可视作值类型(?)可以作为附加属性添加到其他类。复杂类型与实体类型的区别在于复杂类型没有其自己的键。它是依赖于其"宿主"类型跟踪变化 和持久化。一个没有Key属性的类型,并且作为属性映射到一个或多个类型中,Code First就会将其视作为复杂类型。Code First将预设复杂类型的属性出现在宿主类型映射到数据库的表中。”
说简单一点就是,项目中有个类A,这个A,会被其他类引用到比如:实体类B 和 实体类C,但是建立数据库的时候,我们不想为这个分割类A建立表,而是把A类中的属性等建立到 B 和 C 映射的表中,这时候,我们管 A 叫做复杂类型。
来看一下书中的解释和例子:
比如有如下类:
public class Person { public int PersonId { get; set; } public int SocialSecurityNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string StreetAddress { get; set; } public string City { get; set; } public string State { get; set; } public string ZipCode { get; set; } }
这时候,增加Address类作为分割类,不仅可以简化Person类,而且还可以提高Address 的利用,分离后的代码如下:
public class Address { public int AddressId { get; set; } public string StreetAddress { get; set; } public string City { get; set; } public string State { get; set; } public string ZipCode { get; set; } }
public class Person { public int PersonId { get; set; } public int SocialSecurityNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Address Address { get; set; } }
如果这样做的话,CodeFirst 使用默认规则,肯定会产生一个单独的表:Addresses。而我们希望的是 Address中的属性成为表Person中的字段。这时候,我们需要告诉EF,Address类是个复杂类型,就能解决我们现在遇到的问题。
一、使用默认规则:
使用负责类型默认规则,我们需要遵循三个原则:
1、复杂类型无Key属性
2、复杂类型只包含原始属性
3、用作其他类的属性时,属性必须是一个单一实例,不能用于集合类型
上例代码中,我们需要注释掉 :
//public int AddressId { get; set; }
然后在Person中增加构造函数:
public Person() { Address = new Address(); }
这样,运行程序的话,Address类就会被认作是 复杂类型了。
二、DataAnnotation 方式:
DataAnnotation 更加简单 只需要在复杂类型的类上增加属性标签[ComplexType]
[ComplexType] public class Address { public string StreetAddress { get; set; } public string City { get; set; } public string State { get; set; } public string ZipCode { get; set; } }
三、FluntAPI 方式:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.ComplexType<Address>(); base.OnModelCreating(modelBuilder); }
生成的表结构如下图:
我们会发现,生成的表结构的字段名称和类型均是EF默认的,我们当然也可以对其进行配置,我们来用FluntAPI进行配置一下:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { //设置Person 类的表名为 t_People modelBuilder.Entity<Person>().ToTable("t_People"); //设置 PersonId 为主键且自动增长 modelBuilder.Entity<Person>().HasKey(p => p.PersonId) .Property(p => p.PersonId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); //设置Firstname 和 Lastname 的长度为 20 modelBuilder.Entity<Person>().Property(p => p.FirstName).HasMaxLength(20); modelBuilder.Entity<Person>().Property(p => p.LastName).HasMaxLength(20); //设置Address 为复杂类型吗,并设置字段名称及长度 modelBuilder.ComplexType<Address>().Property(a => a.City).HasColumnName("City").HasMaxLength(20); modelBuilder.ComplexType<Address>().Property(a => a.State).HasColumnName("State").HasMaxLength(20); modelBuilder.ComplexType<Address>().Property(a => a.StreetAddress).HasColumnName("Steet").HasMaxLength(30); modelBuilder.ComplexType<Address>().Property(a => a.ZipCode).HasColumnName("ZipCode").HasMaxLength(10); base.OnModelCreating(modelBuilder); }
生成的表结构如下图:
四、复杂类型的嵌套
上面已经指出,复杂类型应该只包括原始属性。但如果在复杂类型中继续引用一个类会产生什么样的结果呢?继续书中的例子:
再创建了两个新类,PersonalInfo 类 和 Measuremet 类,PersonalInfo 包含有两个Measurement 属性。
注意两个类都没有标识属性。我们的意图是两个类都成为复杂类型。PersonalInfo复杂类型使用Measurment复杂类型,这就是所谓的嵌套复杂类型。
public class PersonalInfo { public Measurement Weight { get; set; } public Measurement Height { get; set; } public string DietryRestrictions { get; set; } } public class Measurement { public decimal Reading { get; set; } public string Units { get; set; } }
向Person类中添加新的PersonInfo属性:
public PersonalInfo Info { get; set; }
修改Person类的构造函数:
public Person() { Address = new Address(); Info = new PersonalInfo { Weight = new Measurement(), Height = new Measurement() }; }
然后,我们去掉先前配置的那些 DataAnnotation 和 FluntAPI,采用默认的配置方式,我们运行一下程序,会出现以下错误:
实体类"PersonInfo"没有定义键。请为此实体类定义键。
Code First 并没有将PersonalInfo识虽为复杂类型。原因是打破了规则:复杂类型必须只包含原生类型。在PersonalInfo类中有两个Measurement类型的属性。由于这是非原生类型,规则不能将PersonalInfo作为复杂类型
解决方法:对PersonalInfo 类 配置 [ComplexType] 属性,就能够将属性建立到表中,不需要配置配置Measurement类,因为它遵循复杂类的规则。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
2012-05-21 利用MsChart控件绘制多曲线图表