5

Does EF 6 (code first) supports complex type collection(Value Object collections) mappings? I know that it supports Complex types, but haven't still found an example where we have a collection of complex types.

For instance, suppose you have an entity called Student, which has a collection of contacts. With NH, I can simply say that Student has a collection of contacts and that contact is a component (equivalent to complex type in ef). Can this be done with EF without changing contact to an entity?

Anyname Donotcare
  • 11,113
  • 66
  • 219
  • 392
Luis Abreu
  • 4,008
  • 9
  • 34
  • 63
  • I don't know NH but in EF you can just declare an X-to-X relationship which does what you want, yes. I'm not sure what you mean with "without changing contact to an entity" though. – Jeroen Vannevel Apr 26 '15 at 14:39
  • Hello Jeroen. It's my understanding that when you map an object to a table while using EF, you need to specify a primary key, right? I don't want to do that to Contact because as I said, it behaves as a value object, not as entity – Luis Abreu Apr 26 '15 at 18:27
  • Yes, PK's are typically required [although there seems to be a workaround](http://stackoverflow.com/questions/3996782/entity-framework-table-without-primary-key). Though I would reiterate the advice given there: simply define a key and you'll avoid all the hassle. Is there any benefit to omitting this? – Jeroen Vannevel Apr 26 '15 at 18:31
  • Hello again Jeroen. Well, it all depends. One of the things that I like in NH is the fact that the changes it requires to persist my domain objects are minimum (an ID + version field for optimistic concurrency is all I've needed). Unfortunately, NH development has stopped in time and that is why I'm trying to evaluate EF to see if it's usable in my projects. I can probably add the key, but that is only one of several changes required for me to use EF in one of my real world projects. I'm starting to think that I should probably wait another year before checking i again... – Luis Abreu Apr 27 '15 at 18:01
  • Any updates on this? I'm also trying to map a complex type collection from a view.... – JCS Feb 12 '16 at 20:48
  • 2
    Unfortunately, I believe the answer is no... – Luis Abreu Feb 13 '16 at 21:13
  • What does an NH component collection look like in a relational database? – Gert Arnold Sep 08 '18 at 19:36
  • 1
    it's another table with a foreign key and nh does not enforce a primary key on mapped value type (that's why it's called a value type!) – Luis Abreu Sep 09 '18 at 19:54
  • Well, neither current EF6 nor EF Core support that (EF Core might add support for something like this in the future). – Ivan Stoev Sep 11 '18 at 08:17
  • 1
    I hope they do...meanwhile, I'll keep using NHibernate... – Luis Abreu Sep 12 '18 at 12:24

2 Answers2

3

Complex types are mapped to the entity's table by just adding a column for each property in the complex type. So, with Contact as a separate entity:

public class Student
{
    [Key]
    public int ID {get; set;}
    public Contact PrimaryContact { get; set; }
    public Contact SecondaryContact{ get; set; }
}

[ComplexType]
public class Contact
{
    public string Address{get; set;}
    public string PhoneNumber{get; set;}
    public string Email{get; set;}
}

Will yield a mapping of a Student table that has columns:

ID
PrimaryContact_Address
PrimaryContact_PhoneNumber
PrimaryContact_Email
SecondaryContact_Address
SecondaryContact_PhoneNumber
SecondaryContact_Email

Complex types are just shorthand for declaring the same members wherever you need it. You could use that same Contact type in a bunch of other entities that need contact data without having to explicitly define Address, PhoneNumber, and Email properties for each entity. So you can't really use them in a collection, because every time you wanted to add or remove items to it, you'd have to add or remove columns from the table itself.

public class Student
{
    [Key]
    public int ID {get; set;}
    public ICollection<Contact> Contacts{ get; set; }
}

[ComplexType]
public class Contact
{
    public string Address{get; set;}
    public string PhoneNumber{get; set;}
    public string Email{get; set;}
}

ID
Contacts[x]_Address?
Contacts[x]_PhoneNumber?
Contacts[x]_Email?

Where could Contacts items actually be stored? How could you index it? If you try to do this, the mapper would just ignore the Contacts property altogether.

You can cleanly use collections by just removing the ComplexType from the Contact class. This would create a table in the database, though:

public class Student
{
    [Key]
    public int ID {get; set;}
    public ICollection<Contact> Contacts{ get; set; }
}

public class Contact
{
    [Key]
    public int ID {get; set;}
    public string Address{get; set;}
    public string PhoneNumber{get; set;}
    public string Email{get; set;}
}
AMG
  • 139
  • 13
  • 1
    Mapping a type to a table is the responsibility of the ORM. It's a pitty that EF leaks so much into an exisiting domain. And regarding your question, not sure on what's the confusion here. If A is a type which has a property that references a collection of B and B is a value object (complex type), it's obvious that B doesn't even need to track its corresponding table's id, right? NH does it really well (http://nhibernate.info/doc/nhibernate-reference/components.html)... – Luis Abreu Nov 23 '17 at 09:57
  • They're rhetorical questions. :) They simply can't be answered and illustrate why you can't have collections of complex types. To do what you're asking requires you to make a seperate Contact entity to map to a table to store each item. That page you linked only shows how to have collections WITHIN a composite element, not how to have a collections OF composite elements. – AMG Nov 27 '17 at 19:08
  • In fact, you can't have collections of value types in general. You COULD store a serialized collection in a single property, but that would be absurd as far as purity goes. You can have collections of the B entity in the A entity because each B item is stored in B's table as a separate row with it's own ID. That ID is what gets stored in A. That page only mentions collections WITHIN composite elements, not collections OF composite elements. I know nothing about NHibernate, but it would have to have a place to put B's items too- in a separate table. Which would be the same as another EF entity. – AMG Nov 27 '17 at 19:19
  • 1
    Section 7.2 talks about it. And yes, b is saved into another table, but its tracking is completely different from an entity because it's a value object! – Luis Abreu Nov 27 '17 at 23:51
2

Apparently NHibernate is more flexible in that regard, because at the time of writing (EF6.2 and EF Core 2.1), neither EF6 nor EF Core support complex (or more generally primitive or value object) type collection mapping.

EF Core is even worse because the Owned Entity Types, supposedly to be the replacement of the EF Complex Types really have more entity like behavior (from the change tracking perspective) and cannot be used as let say DDD immutable multi property value objects.

The only workaround I know is to map the value object/collection representation in some serialization format ((e.g. XML, JSON, binary etc.) to a single database string/binary column. While this would work for reading/storing the data, it lacks the query capabilities, so IMO is not a serious option.

Speaking about EF6, I don't think it will ever get that support. EF6 is basically in maintenance mode and won't get future major improvements.

EF Core looks more promising in that regard because the Support for collections of owned entities is scheduled for the next EF 2.2 release. However it's unknown how they will implement them (initially), and taking into account how they did the owned types, it might not be the way you expect, so it can't be said in advance if they would work for value object collection scenarios.

I know this is not the answer you wanted, but that's the reality.

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343