I am using EF7 and noticed that there was a ComplexType in EF6 which was Missing in EF7 and is added to EF8 again. So in the EF7, an OwnsOne function is used to implement value objects like complex types. So, I am curious to learn what is the difference between OwnsOne and ComplexType and if there is any special and important should I Upgrade my dotnet and EF versions to 8.
This is at least partially covered in the What's New in EF Core 8 doc (emphasis mine):
Objects saved to the database can be split into three broad categories:
- Objects that are unstructured and hold a single value. For example,
int
,Guid
,string
,IPAddress
. These are (somewhat loosely) called "primitive types".- Objects that are structured to hold multiple values, and where the identity of the object is defined by a key value. For example,
Blog
,Post
,Customer
. These are called "entity types".- Objects that are structured to hold multiple values, but the object has no key defining identity. For example,
Address
,Coordinate
.Prior to EF8, there was no good way to map the third type of object. Owned types can be used, but since owned types are actually entity types, they have semantics based on a key value, even when that key value is hidden.
EF8 now supports "Complex Types" to cover this third type of object. Complex type objects:
- Are not identified or tracked by key value.
- Must be defined as part of an entity type. (In other words, you cannot have a
DbSet
of a complex type.)- Can be either .NET value types or reference types.
- Instances can be shared by multiple properties.
One of the results for this difference is that owned types can be stored in a separate table (see the Storing owned types in separate tables section of the docs):
Also unlike EF6 complex types, owned types can be stored in a separate table from the owner.
While Complex Types (ATM) do not (from the announcement):
We don’t plan to allow complex types to be mapped to their own table. However, in a future release, we do plan to allow the complex type to be saved as a JSON document in a single column.
This results that currently Complex Types for example does not support collections (see the limitations section) while for Owned Types it is possible via a separate table (Collections of owned types)
Another difference is that the same instance of complex type can be used with different entities. See the simple example from the docs:
Now let’s say we want to ship an order to a customer and use the customer’s address as both the default billing an shipping address. The natural way to do this is to copy the
Address
object from theCustomer
into theOrder
. For example:
var customer = new Customer
{
Name = "Willow",
Address = new() { Line1 = "Barking Gate", City = "Walpole St Peter", Country = "UK", PostCode = "PE14 7AV" }
};
context.Add(customer);
customer.Orders.Add(
new Order { Contents = "Tesco Tasty Treats", BillingAddress = customer.Address, ShippingAddress = customer.Address, });
await context.SaveChangesAsync();
Which will fail for owned types but will work for complex ones.