Home > database >  EF Core lazy loader behaviour with new entities
EF Core lazy loader behaviour with new entities

Time:01-02

The EF Core docs on lazy loading without proxies reference this example:

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;
    }
}

So we have a parameterless constructor and the lazy loader injection constructor. The docs then say this:

This method doesn't require entity types to be inherited from or navigation properties to be virtual, and allows entity instances created with new to lazy-load once attached to a context.

So what does this mean exactly for new instances? An entity created by EF core will have the lazy loader injected but a new entity will not. Does this imply that once a new item is added to the context the lazy loader backing field should get updated?

CodePudding user response:

EF in a way breaks the rules of encapsulation to a degree when it comes to private constructors and setters. It's not a bad thing since the real goal of making these private is to ensure the access to your code is organized without the clutter of guessing which and what to use, but at the same time EF will ignore those limitations via reflection.

When you Attach an entity to a DbContext, that DbContext will check the attached entity for property types it can inject. This includes including the ILazyLoader which EF will use the private setter to wire up. So you can new up a new entity, and once attached to a DbContext it will have access to the lazy loader.

CodePudding user response:

So what does this mean exactly for new instances? An entity created by EF core will have the lazy loader injected but a new entity will not. Does this imply that once a new item is added to the context the lazy loader backing field should get updated?

This is exactly what it means. Note that attached to a context here

allows entity instances created with new to lazy-load once attached to a context.

means tracked (the usual term they use in most of the places), in other words, any operation which adds the entity to the context change tracker - Attach, Add, Update, Remove, setting EntityEntry.State to anything except EntityState.Detached, entity created by tracking query etc.

Unfortunately the documentation does not explain all the details, so following is some additional information retrieved by experiment:

First, as explained above, the lazy loader service property is set when the entity is tracked. But the opposite is also true - the property is nulled out when entity is untracked, for instance by setting State to EntityState.Detached.

Second is the actual requirements for the property itself. The docs say at the end:

Note

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.

which is not exactly correct.

The actual requirement is to have

  • property with getter and setter
  • with any visibility (usually private)
  • with case insensitive name "LazyLoader"
  • and type either ILazyLoader or Action<object, string>

At the same time, the "injection" constructor with the corresponding argument is not required. When creating entity instances, EF Core will use any constructor and set the lazy loader service property the same way as when it tracks an existing entity instance.

  • Related