EF Core HasMany vs OwnsMany
Asked Answered
C

2

33

For one to many relationship, what's the difference between HasMany and OwnsMany? When should I use one over another?

For example:

public class xxx 
{
    public virtual IReadOnlyCollection<xxxHistoryEntity> Histories => _histories;
    private readonly List<xxxHistoryEntity> _histories = new List<xxxHistoryEntity>();
}

public class xxxHistoryEntity : Entity<string>
{
    public string State { get; set; }
    public string NodeId { get; set; }
    public string Message { get; set; }
}

The Entity Configuration:

class xxxConfiguration
    : IEntityTypeConfiguration<xxx>
{
    public void Configure(EntityTypeBuilder<xxx> builder)
    {
        builder.OwnsMany(itm => itm.Histories, collbuilder =>
        {
            collbuilder.HasForeignKey("xxxid");
        });
    }
}

class xxxHistoryConfiguration
    : IEntityTypeConfiguration<xxxHistoryEntity>
{
    public void Configure(EntityTypeBuilder<xxxHistoryEntity> builder)
    {
        builder.ToTable("xxx_histories");
        builder.HasKey(itm => itm.Id);
        builder.Property(itm => itm.Id)
             .ValueGeneratedOnAdd();
    }
}

The generated migration is below:

        migrationBuilder.CreateTable(
            name: "xxx_histories",
            columns: table => new
            {
                id = table.Column<string>(nullable: false),
                xxxid = table.Column<string>(nullable: false),                    
                state = table.Column<string>(nullable: true),
                nodeid = table.Column<string>(nullable: true),
                message = table.Column<string>(nullable: true),
                xmin = table.Column<uint>(type: "xid", nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_xxx_histories", x => new { x.id, x.xxxid });
                table.ForeignKey(
                    name: "fk_xxxhistoryentity_xxx_xxxarid",
                    column: x => x.xxxid,
                    principalTable: "xxx",
                    principalColumn: "id",
                    onDelete: ReferentialAction.Cascade);
            });

if I update the xxxConfiguration by replacing the OwnsMany with HasMany, like:

class xxxConfiguration
    : IEntityTypeConfiguration<xxx>
{
    public void Configure(EntityTypeBuilder<xxx> builder)
    {
        builder.HasMany(itm => itm.Histories)
            .WithOne()
            .HasForeignKey("xxxid");
    }
}

The generated migration is below:

        migrationBuilder.CreateTable(
            name: "xxx_histories",
            columns: table => new
            {
                id = table.Column<string>(nullable: false),
                xxxid = table.Column<string>(nullable: false),
                state = table.Column<string>(nullable: true),
                nodeid = table.Column<string>(nullable: true),
                message = table.Column<string>(nullable: true),
                xmin = table.Column<uint>(type: "xid", nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_xxx_histories", x => new { x.id, x.xxxid });
                table.ForeignKey(
                    name: "fk_xxxhistoryentity_xxx_xxxid",
                    column: x => x.xxxid,
                    principalTable: "xxx",
                    principalColumn: "id",
                    onDelete: ReferentialAction.Cascade);
            });

As you can see, the migration generated by both are the same. So what's the point of OwnsMany?

Ceria answered 23/10, 2019 at 6:38 Comment(0)
S
17

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

Southsouthwest answered 23/10, 2019 at 6:42 Comment(11)
I did read the document before, I know it's for a concept like aggregate or value object, but I still don't get what's the obvious differences when I use "OwnsMany". Is there anything I can't do just use HasMany?Ceria
If you use OwnsMany, you will only be able to access objects of this class as navigation properties of another class.Southsouthwest
Thanks, so based on the example in my question, do you mean I can only access the histories from xxx object if I use OwnsMany? But I think I can directly access the histories like dbContext.Histories if I expose the histories like ' DbSet<xxxHistoryEntity> Histories' in my dbContext. What I missed?Ceria
More reading: The Owns API typically represents DDD Aggregation or UML CompositionIvetteivetts
@Ceria In the documentation now it reads: You cannot create a DbSet<T> for an owned type. You cannot call Entity<T>() with an owned type on ModelBuilderStriped
Suppose I have a Foo class and a FooType (FOO_TYPE: ID, NAME). Will Entity<Foo>.HasOne(f=>f.FooType) or Entity<Foo>.OwnsOne(f=>f.FooType)?Nembutal
@serge, unclear question. Will what? Has relations preserve entity DbSets, while Owns relations makes entities not need a DbSet and be accessible only as part of the owner entity, in whichever way it's mapped inside it (depending on your use of the model builder or entity type builder).Cirro
@Paul-SebastianManole "Will" ... "HasOne" or "Will" ... "OwnsOne"Nembutal
@serge, will has one or will owns one what man? Are you not a native English speaker and is that why we're not understanding each other? Let's simplify. You said "Will Entity<Foo>.HasOne(f=>f.FooType) or Entity<Foo>.OwnsOne(f=>f.FooType)?". Which is basically "Will A or B?", but in that question will A or B do what? That's why your question is unclear.Cirro
There is not question about native English speaker, because the functions name are immutable, if you want more clarity, "Will" the function "HasOne" or "OwnsOne" "return true" in the cases I mentioned.Nembutal
@serge, this is much clearer now. Nowhere in your original comment did you mention anything about the return value. With your final clarification your sentence now seems complete, and makes sense because it expresses a complete thought. Sorry we dragged this out in the comments, but I was really interested in this SO question and its comments more or less made sense to me, except yours.Cirro
E
7

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

Exultant answered 25/8, 2022 at 9:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.