1

TL;DR: what's the most concise method to load a single navigation property on an entity?

Suppose I already have an instance entity Foo with a child Child. Instance of Foo I have has ChildId set but Child was not loaded, i.e. foo.ChildId == 1234 but foo.Child == null.

I want to get Child if it is missing. I know I can do:

if (foo.Child is null) { foo.Child = _dbContext.Foos.Include(f => f.Child).Single(f => f.Id == foo.Id).Child; } 

but I am looking for a lazy way (pun!) to load it on-demand (I don't want to load all properties on-demand, however, just the one I want to load explicitly), something like:

var child = _dbContext.EnsureLoaded(da, e => e.Child); 

Is there a way to do this?

1
  • Have you checked Lazy Loading in the docs? If Child is null, it means Lazy Loading wasn't enabled and the Child property isn't virtual. Commented Jan 26, 2023 at 14:48

2 Answers 2

4

Probably you are looking for Explicit Loading of Related Data

_dbContext.Entry(foo).Reference(f => f.Child).Load(); 
Sign up to request clarification or add additional context in comments.

Comments

2

Lazy Loading is already available. There are two options:

  • using proxy objects generated by EF Core to automagically load related entities or
  • use the ILazyLoader service with POCOs to load related entities when requested

Proxies

To use proxies, the DbContext has to be configured first :

.AddDbContext<BloggingContext>( b => b.UseLazyLoadingProxies() .UseSqlServer(myConnectionString)); 

After that, any properties that need to be lazy loaded have to be made virtual :

public class Blog { public int Id { get; set; } public string Name { get; set; } public virtual ICollection<Post> Posts { get; set; } } public class Post { public int Id { get; set; } public string Title { get; set; } public string Content { get; set; } public virtual Blog Blog { get; set; } } 

At runtime EF will return proxy objects that inherit from the entity classes and overload the lazy properties to load the related object when first requested.

ILazyLoader service

Another option, that doesn't require inheritance, is to use POCOs and the ILazyLoader service to load the entities when needed :

public class Blog { private ICollection<Post> _posts; public Blog() { } private Blog(ILazyLoader lazyLoader) { LazyLoader = lazyLoader; } private ILazyLoader LazyLoader { get; set; } public int Id { get; set; } public string Name { get; set; } public ICollection<Post> Posts { get => LazyLoader.Load(this, ref _posts); set => _posts = value; } } 

This adds a dependency on the ILazyLoader interface itself, which in turn adds a dependency to EF Core in domain or business models.

This can be avoided by injecting the loader as a lambda, along with some convention magic :

public class Blog { private ICollection<Post> _posts; public Blog() { } private Blog(Action<object, string> lazyLoader) { LazyLoader = lazyLoader; } private Action<object, string> LazyLoader { get; set; } public int Id { get; set; } public string Name { get; set; } public ICollection<Post> Posts { get => LazyLoader.Load(this, ref _posts); set => _posts = value; } } 

This is used in combination with an extension method that actually calls the loader using the property's name and sets its backing field :

public static class PocoLoadingExtensions { public static TRelated Load<TRelated>( this Action<object, string> loader, object entity, ref TRelated navigationField, [CallerMemberName] string navigationName = null) where TRelated : class { loader?.Invoke(entity, navigationName); return navigationField; } } 

As the docs warn:

The constructor parameter for the lazy-loading delegate must be called "lazyLoader". Configuration to use a different name than this is planned for a future release.

1 Comment

Thank you @Panagiotis Kanavos, I am aware of lazy loading (I might have mentioned that in the question), the reason I don't want props to be loaded automatically is because of the serialization and mapping. Which is why I was looking for a method to load data explicitly. But your answer I will be able to apply in other situations in the future, I'm sure.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.