Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EFCore - How to exclude owned objects from automatic loading?

I'm trying to wrap my head around EF Cores owned objects and how i can control when to load certain chunks of data.

Basically i'm having a bunch of old legacy tables (some with ~150 columns) and want to model them using a root entity and several owned objects per table to achieve better segmentation and bundle certain functionalities. Example: There is an "article" entity containing ~20 properties for the most important fields of the underlying table. That entity also contains an OwnedObject "StorageDetails" wrapping a dozen more fields (and all the functions concerned with storing stuff).

Problem: I can't find a way to control if an owned object should be loaded immediatly or not. For some of them i would prefer to load them explicitly using Include() ...

public class Article : EntityBase
{  
    public string ArticleNumber { get;set; }

    // Owned object, shares article number as key.
    public StorageDetails StorageStuff { get; set; }

    // An Entity from another table having a foreign key reference 
    public SomeOtherEntity OtherStuff { get; set; }
}

public class StorageDetails : OwnedObject<Article>
{
    public Article Owner { get; set; }
}

// Somewhere during model creation ...
builder.OwnsOne(article => article.StorageStuff);

builder.HasOne(article => article.OtherStuff ) 
       ...

Defining the model with OwnsOne and loading an article immediatly loads the StorageStuff. To load the OtherThing i have to Inlcude() it in a query, which is basically what i want to achieve for the owned object.

Is that possible? If not, what other approach could you point me to?

like image 275
Lupf Avatar asked Jan 04 '19 15:01

Lupf


Video Answer


1 Answers

Update: I wasn't exact right - the implicit eager loading of owned entity types is really a default and can be changed via SetIsEagerLoaded metadata API (EF Core 3.0+)

modelBuilder.Entity<Article>().OwnsOne(e => e.StorageStuff)
    .Metadata.PrincipalToDependent?.SetIsEagerLoaded(false);

or AutoInclude fluent API (EF Core 5.0+)

modelBuilder.Entity<Article>().Navigation(e => e.StorageStuff)
    .AutoInclude(false);

This allows eager loading them via Include / ThenInclude. But due to EF Core implementation specifics, owned entity types cannot use explicit/lazy loading. Trying to do so leads to runtime exception. So my recommendation from the original answer still applies.

Original: With owned types - it's not possible (currently), because this behavior is "by design". And is documented in Querying owned types section of the EF Core documentation:

When querying the owner the owned types will be included by default. It is not necessary to use the Include method, even if the owned types are stored in a separate table.

It's a bit vague by saying "by default", but you can safely read it as "always", because there is no option or Exclude method.


Since currently the only way to control loading related data is navigation property to a real entity, make the types you want to control a "real entities", i.e. don't mark them as owned, define explicit or shadow PK, and map these "entities" with Table Splitting:

It is now possible to map two or more entity types to the same table where the primary key column(s) will be shared and each row will correspond to two or more entities.

To use table splitting an identifying relationship (where foreign key properties form the primary key) must be configured between all of the entity types sharing the table:

like image 195
Ivan Stoev Avatar answered Nov 11 '22 23:11

Ivan Stoev