Skip to content

Settings.UseLazyLoading

Simon Hughes edited this page Aug 30, 2026 · 1 revision

Settings.UseLazyLoading

Marks every navigation property virtual, which is what EF needs before it can load related data on first access.

Type bool
Default false
Applies to EF 6 and EF Core
Databases All
In Database.tt? Yes

What it does

Lazy loading means order.Customer runs a query the first time you touch it, rather than being loaded up front. EF implements it by subclassing your entity at run time and overriding the navigation properties - which it can only do if they are virtual.

That is all this setting does: add or omit the virtual keyword.

It is not the whole feature. On EF Core you also need a package and a call; see below. On EF 6, virtual is enough.

Example

Settings.UseLazyLoading = false (default)

    // Product
    public class Product
    {
        public int ProductId { get; set; } // ProductId (Primary key)
        public string ProductName { get; set; } // ProductName (length: 100)
        public decimal UnitPrice { get; set; } // UnitPrice
        public string Notes { get; set; } // Notes
        public int CategoryId { get; set; } // CategoryId
        public string DisplayLabel { get; private set; } // DisplayLabel (length: 150)

        // Foreign keys

        /// <summary>
        /// Parent Category pointed by [Product].([CategoryId]) (FK_Product_Category)
        /// </summary>
        public Category Category { get; set; } // FK_Product_Category

        public Product()
        {
            UnitPrice = 0m;
        }
    }

Settings.UseLazyLoading = true

    // Product
    public class Product
    {
        public int ProductId { get; set; } // ProductId (Primary key)
        public string ProductName { get; set; } // ProductName (length: 100)
        public decimal UnitPrice { get; set; } // UnitPrice
        public string Notes { get; set; } // Notes
        public int CategoryId { get; set; } // CategoryId
        public string DisplayLabel { get; private set; } // DisplayLabel (length: 150)

        // Foreign keys

        /// <summary>
        /// Parent Category pointed by [Product].([CategoryId]) (FK_Product_Category)
        /// </summary>
        public virtual Category Category { get; set; } // FK_Product_Category

        public Product()
        {
            UnitPrice = 0m;
        }
    }

One keyword on one property. On Category the reverse navigation collection gets the same treatment.

Turning it on properly

EF Core needs the proxies package and a call when you configure the context:

Install-Package Microsoft.EntityFrameworkCore.Proxies
services.AddDbContext<MyDbContext>(options => options
    .UseSqlServer(connectionString)
    .UseLazyLoadingProxies());

Add MultipleActiveResultSets=True to the connection string as well, or a lazy load triggered while you are still reading the outer result set will fail.

EF 6 enables lazy loading by default once the properties are virtual. Add MultipleActiveResultSets=True there too.

When to use it

Rarely, and deliberately. Lazy loading is convenient in a desktop application or a script where the context is long-lived and you are exploring the object graph interactively.

It is a poor fit for web applications, which is most of the code that reaches this generator:

  • Every property access can be a query. A loop over 100 orders that touches order.Customer is 101 queries, and it looks like ordinary property access, so it does not read as a performance problem.
  • Serialisation walks the whole graph. Returning an entity from an API endpoint touches every navigation property, each of which loads, whose properties are then touched in turn. People have loaded most of a database from one endpoint this way.
  • The context is gone by the time you need it. Lazy load after the request has ended and you get ObjectDisposedException, often only under load.

The alternative is eager loading, which is explicit and therefore reviewable:

var orders = context.Orders
    .Include(o => o.Customer)
    .Include(o => o.OrderLines)
    .AsNoTracking()
    .ToList();

Gotchas

virtual alone does nothing on EF Core. Without UseLazyLoadingProxies() the properties are virtual and never populated, so they read as null and you conclude the data is missing. This is the single most common confusion with the setting.

Proxies change the runtime type. order.GetType() returns a generated proxy type, not Order. Code that switches on the exact type, or serialisers configured by type name, will notice.

It applies to all navigation properties or none. There is no per-relationship control. If you want virtual on some only, generate without it and add the keyword in a partial class - or reconsider whether you want lazy loading at all.

[JsonIgnore] is the usual first aid if you have lazy loading on and an API that serialises entities. See Settings.AdditionalReverseNavigationsDataAnnotations. Returning DTOs instead is the real fix.

See also

Clone this wiki locally