Today we're going to talk about a feature I was completely unaware of until recently: the owned property in Entity Framework Core.
Table of Contents
1 - What is the Owned Property in Entity Framework Core?
The owned property is a tag you assign to an entity, which makes that entity completely dependent on another entity, since this new entity depends on its parent.
For example, imagine we have users and addresses, each user can have a single address, so you might have something like this in your code:
public class User : CursoEFBaseEntity<int>
{
public string UserName { get; set; }
[MaxLength(50)]
public string Email { get; set; }
public Address Address { get; set; } // 👈 this
public virtual ICollection<Wokringexperience> Wokringexperiences { get; set; }
}
You might think that the Address
property inside our user is a separate table within the database with a FK (foreign key); but it's not. In this case, the Address
entity is tagged with [Owned]
:
[Owned]
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
}
Internally, we're not creating a new table in the database, but instead creating new columns in the table, in this case called Address_Street
, Address_City
, Address_PostalCode
, Address_Country
, etc.
Just like with any other property, we have to run migrations using dotnet ef migrations add IncludeOwnedAddress
.
1.1 - When Should You (or Not) Use the Owned Property?
The fact that it is a property completely dependent on another gives us some hints about when to use it or not. Being dependent means you can't reference it by ID in another entity. For example, if you delete the parent entity, this one is also deleted since it is a part of it.
In my opinion, the owned property is only useful when you want to represent composite values within an entity.
For other cases, it doesn't make much sense since you can't (or shouldn't) do a join and they aren't independent properties or entities, which means you can't have a DbSet<T>
for it.
Also, an owned entity can't derive from another.
2 - JSON Columns
If the idea is to group multiple properties into an object, why not do the same in the database and read straight from a JSON?
Since .NET 9, we can do this—just indicate it in the OnModelCreating
of your DbContext
:
modelBuilder.Entity<User>()
.OwnsOne<Address>(user=>user.Address)
.ToJson();
Careful, if you have old Data Seeds where you used HasData
, since .NET 9 there's a new way, because HasData
is not compatible with JSON columns. The linked post explains exactly how to do it.
Alternatively, if you just want to store the results as plain text in a JSON, you can save it as text and create the conversion inside EF:
builder.Property(u => u.Address)
.HasConversion(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject<Address>(v));
But for this last approach you do not need owned, and you also lose the benefits of querying inside the column. In reality, this part has NOTHING to do with owned, but it's a common practice and that's why I considered including the explanation here.
2.1 - Queries on JSON Fields
The owned property lets us run queries using LINQ, which the code itself will translate to SQL.
For example, in the following code:
List<User> dublinUsers = await Entities
.Where(a => a.Address.City == "Dublin")
.ToListAsync()
Entity Framework translates the comparison to JSON_VALUE(...) = 'Dublin';
As you can imagine, this is not very efficient, so if you have to make this type of query very frequently, it's recommended not to use a JSON column. On the other hand, if queries are rare, it shouldn't be a problem.
Conclusion
The Owned property allows us to have objects (or value objects) inside Entity Framework entities without needing to create a table for them. These are data that don't have their own identity and live and die with the parent entity.
We have two options: create flat columns or a JSON column. Which one you use should depend on whether you're going to run queries or not, and how often, always prioritizing performance.
Finally, before complicating your model, think if you really need a complex object or if two or three “normal” columns will work just as well.
If there is any problem you can add a comment bellow or contact me in the website's contact form