复合类型(Complex types)
复合类型(Complex Types)跟Entity类型的区别在于,复合类型(Complex Types)没有Key。复合类型跟踪改变和存储是要依赖宿主类型的。
从代码来看就更直观。
首先,假设我们有一个Person类,这个类包含了SSN,FirstName,LastName和Address的信息。代码如下
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类,来存储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; } }
如果通过上面的两个类,Code Fist将在数据库中生成两张表,而这又不是我们想要的结果。在数据库层面我们只是想有一张表,而在代码层面,我们希望可以有单独的Address类来存储Address信息。这时候,我们希望把Address定义为一个复合类型,而不是entity类型。
要如何做才能把Address类变成复合类型呢?复合类型和entity类型的区别在于,复合类型没有key。所以,我们只需要把AddressId从Address类中去掉就可以了。
在做插入操作时,出现来另外一个问题,在Person类中的Address如果为NULL时,当调用SaveChanges方法时将会抛出一个DbUpdateException。解决方法也很简单,在Person类的构造函数中,实例化一个Address的实例赋值给Address属性。
另外两个规则应用于复合类型。其一,复合类型只能包含原始(primitive)数据类型;其二,当被其他类使用时,不能是集合类型。换言之,如果Person类中的是一个List<Address>或者另外一个集合类型存在于Address类中,Address将不被视为复合类型。
如果你一定要使用AddressId时,可以通过DataAnnotation,使用[ComplexType]修饰Address类。Fluent API实现方式: modelBuilder.ComplexType<Address>();
让我们来看一个更复杂的复合类型的例子
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; } }
我想让PersonalInfo和Measurement都是复合类型。两个类都没有Key,同样在Person类的构造函数里面也将这两个类型实例化并赋值到对应的属性上。但在运行时依旧会报错。因为PersonalInfo并没有完全满足复合类型的条件——使用原始数据类型。只需要在PersonalInfo类型上面加上[ComplexType]的修饰,就能够使代码正常工作了。
同样的,DataAnnotation和FluentAPI都可以对复合类型里面的属性进行配置。