Condividi tramite


Caricamento differita dei dati correlati

Caricamento differito con proxy

Il modo più semplice per usare il caricamento differito consiste nell'installare il pacchetto Microsoft.EntityFrameworkCore.Proxies e abilitarlo con una chiamata a UseLazyLoadingProxies. Per esempio:

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

In alternativa, quando si usa AddDbContext:

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

EF Core abiliterà quindi il caricamento differito per qualsiasi proprietà di navigazione che può essere sovrascrivibile, ovvero deve essere virtual e in una classe che può essere ereditata. Nelle seguenti entità, ad esempio, le proprietà di navigazione Post.Blog e Blog.Posts verranno caricate in modo differito.

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

Avviso

Il caricamento differito può causare l'esecuzione di round trip aggiuntivi del database (il cosiddetto problema N+1), e si dovrebbe prestare attenzione per evitarlo. Per altri dettagli, vedere la sezione relativa alle prestazioni .

Caricamento differito senza proxy

Il caricamento differito senza proxy funziona inserendo il servizio ILazyLoader in un'entità, come descritto in Costruttori di tipi di entità. Per esempio:

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

Questo metodo non richiede che i tipi di entità siano ereditati o che le proprietà di navigazione siano virtuali e consente alle istanze di entità create con new di effettuare il caricamento differito una volta collegate a un contesto. Tuttavia, richiede un riferimento al ILazyLoader servizio, definito nel pacchetto Microsoft.EntityFrameworkCore.Abstractions . Questo pacchetto contiene un set minimo di tipi in modo da avere un impatto minimo a seconda di esso. Tuttavia, per evitare completamente di dipendere da qualsiasi pacchetto EF Core nei tipi di entità, è possibile introdurre il metodo ILazyLoader.Load come delegato. Per esempio:

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

Il codice precedente usa un Load metodo di estensione per rendere l'uso del delegato un po' più chiaro:

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

Annotazioni

Il parametro del costruttore per il delegato lazy-loading deve essere denominato "lazyLoader". Configurazione per l'uso di un nome diverso da quello previsto per una versione futura.