Lazy Loading zugehöriger Daten

Lazy Loading mit Proxys

Die einfachste Möglichkeit für die Verwendung von Lazy Loading besteht in der Installation des Pakets Microsoft.EntityFrameworkCore.Proxies und der Aktivierung dieses Pakets durch Aufrufen von UseLazyLoadingProxies. Beispiel:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseLazyLoadingProxies()
        .UseSqlServer(myConnectionString);

Oder bei Verwendung von AddDbContext:

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

EF Core aktiviert Lazy Loading anschließend für beliebige überschreibbare Navigationseigenschaften – diese müssen virtual und in einer Klasse enthalten sein, aus der sie geerbt werden können. In den folgenden Entitäten wird beispielsweise Lazy Loading für die Navigationseigenschaften Post.Blog und Blog.Posts durchgeführt.

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

Warnung

Verzögertes Laden kann zu unnötigen zusätzlichen Datenbankroundtrips führen (das sogenannte N+1-Problem). Sie sollten Maßnahmen ergreifen, um dies zu vermeiden. Weitere Details finden Sie im Abschnitt Vermeiden von verzögertem Laden.

Lazy Loading ohne Proxys

Beim Lazy Loading ohne Proxys wird der Dienst ILazyLoader, wie unter Entitätstypenkonstruktoren beschrieben, in eine Entität eingefügt. Beispiel:

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

public class Post
{
    private Blog _blog;

    public Post()
    {
    }

    private Post(ILazyLoader lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private ILazyLoader LazyLoader { get; set; }

    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog
    {
        get => LazyLoader.Load(this, ref _blog);
        set => _blog = value;
    }
}

Für diese Methode ist nicht erforderlich, dass aus Entitätstypen vererbt wird oder Navigationseigenschaften „virtual“ sind. Mit new erstellte Entitätsinstanzen können Lazy Loading ausführen, sobald sie einem Kontext angefügt wurden. Erforderlich ist ein Verweis auf den ILazyLoader-Dienst, der im Microsoft.EntityFrameworkCore.Abstractions-Paket definiert ist. Dieses Paket enthält eine minimale Gruppe von Typen, sodass die Abhängigkeit davon kaum Auswirkungen hat. Es ist jedoch möglich, die Methode ILazyLoader.Load als Delegat einzufügen, um eine Abhängigkeit von EF Core-Paketen in den Entitätstypen gänzlich zu vermeiden. Beispiel:

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

public class Post
{
    private Blog _blog;

    public Post()
    {
    }

    private Post(Action<object, string> lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private Action<object, string> LazyLoader { get; set; }

    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog
    {
        get => LazyLoader.Load(this, ref _blog);
        set => _blog = value;
    }
}

Der oben stehende Code verwendet eine Load-Erweiterungsmethode, um die Verwendung des Delegaten sauberer zu gestalten:

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

Hinweis

Der Konstruktorparameter für den Lazy Loading-Delegaten muss als „lazyLoader“ bezeichnet werden. Eine Konfiguration für die Verwendung eines anderen Namens ist für ein zukünftiges Release geplant.