EF Core HasMany vs OwnsMany

EF Core HasMany vs OwnsMany

回答1

From documentation:

EF Core allows you to model entity types that can only ever appear on navigation properties of other entity types. These are called owned entity types. The entity containing an owned entity type is its owner.

Owned entities are essentially a part of the owner and cannot exist without it, they are conceptually similar to aggregates.

https://learn.microsoft.com/en-us/ef/core/modeling/owned-entities

 

回答2

One of the differences is that relationships configured with OwnsMany() will include the owned entities by default when querying the owner from the database, whilst when using WithMany() you have to specify AutoInclude() manually if you want them to be included every time you get the owner entity form the database.

Also from documentation: Querying owned types

 

EF Core: Owned Entity Types

In this post let's see what is Owned Entity Types in Entity Framework Core. This is a very nice feature when it comes to DDD.
 
Owned Entities are entities that can be only appeared on navigation properties of other entity types. Basically, they can't exist without the Owner.  
 
Let's go by an example. Consider the following two entities.
public class User
{
    public int Id { getset; }
 
    public string Name { getset; }
 
    public Address Address { getset; }
}
public class Address
{
    public string Street { getset; }
 
    public string City { getset; }
}
And my DbContext looks like this.
public class MyDbContext : DbContext
{
    public DbSet<User> Users { getset; }
 
    //...
}
So here, we can use OwnsOne to configure the Address Owned Entity to the Owner User as below (I am using Fluent API instead of annotations, you can annotations if you prefer, but I prefer Fluent API all the time).
public class UserEntityConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.ToTable($"{nameof(User)}");
 
        builder.OwnsOne(x => x.Address);
    }
}

 

If you don't like the default naming convention, you can always override the behavior like below.
public class UserEntityConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.ToTable($"{nameof(User)}");
 
        builder.OwnsOne(x => x.Address, y =>
        {
            y.Property(y => y.City)
                    .HasColumnName("City");
 
            y.Property(y => y.Street)
                    .HasColumnName("Street");
        });
    }
}
And this would give something like below.
 
 
Now let's see consider the following code to insert some data and retrieval.
using var context = new MyDbContext();
 
User user = new User
{
    Name = "John Doe",
    Address = new Address { Street = "Some Street1", City = "Some City1" }
};
 
await context.Users.AddAsync(user);
await context.SaveChangesAsync();
using var anotherContext = new OrderDbContext();

user = await anotherContext.Users.FirstOrDefaultAsync();
Here for selection, EF Core will generate a query as below.
SELECT TOP(1[u].[Id][u].[Name][u].[City][u].[Street]
FROM [User] AS [u]
 
 
 
So here you should be able to see a very important thing. When we are getting User, we didn't have to explicitly Include Address to load Address details, it's loading its Owned Entities by default. This might not be a good example, since it's in the same table, we will see a clearer example when we are going through OwnsMany later in this post. 
 
Note: I am using anotherContext here for retrieval, because EF Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.
Above was kind of a one-to-one relationship. Now let's have a look at one-to-many type of relationship.
public class User
{
    public int Id { getset; }
 
    public string Name { getset; }
 
    public ICollection<Address> Addresses { getset; }
}
public class Address
{
    public string Street { getset; }
 
    public string City { getset; }
 
    public string Type { getset; }
}
Now I can use OwnsMany as below.
public class UserEntityConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.ToTable($"{nameof(User)}");
 
        builder.OwnsMany(x => x.Addresses, y =>
        {
            y.ToTable("UserAddress");
 
            y.Property(y => y.City)
                .HasColumnName("City");
 
            y.Property(y => y.Street)
                .HasColumnName("Type");
            y.Property(y => y.Street)
                .HasColumnName("Type");
        });
    }
}

 

 

And here also, without doing .Include(x => x.Addresses), the addresses are being returned.
 
So that's about it. You can read more on Owned Entity Types by going to the below link.
There are some restrictions when it comes to Owned Entities which makes perfect sense.
  • You cannot create a DbSet<T> for an owned type.
  • You cannot call Entity<T>() with an owned type on ModelBuilder.
  • Instances of owned entity types cannot be shared by multiple owners (this is a well-known scenario for value objects that cannot be implemented using owned entity types).
Hope this helps.
 
Happy Coding.
 
Regards,
Jaliya
 
 
 

 

 

 

 

 

 

 

 

 

作者:Chuck Lu    GitHub    
posted @   ChuckLu  阅读(509)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2021-11-15 高等数学 工专 柳重堪
2021-11-15 TortoiseGit not showing icon overlays Git图标消失
2021-11-15 Node.js: Python not found exception due to node-sass and node-gyp
2018-11-15 kentico检查当前授权用户,是否为admin角色
2016-11-15 signtool
点击右上角即可分享
微信分享提示