-
Notifications
You must be signed in to change notification settings - Fork 226
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 |
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.
// 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;
}
} // 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.
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.
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.Customeris 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();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.
- Lazy Loading - the full setup, including the warnings above at length
- Settings.AdditionalReverseNavigationsDataAnnotations - keeping serialisers out of the graph
- Settings.ForeignKeyFilterFunc - removing reverse navigations entirely
-
Connection strings - where
MultipleActiveResultSetsgoes - Settings Reference
- Settings A-Z - every setting, with a page each
- Common Settings Types Explained
- Settings Callbacks
- Settings runtime values and helpers
- Filtering
- Full Control Over the Generated Code
- Enum Generation from Table Data
- Owned Entities
- JSON column support
- Global Query Filters
- Extended Property Names Feature
- Partial Properties
- File-Scoped Namespaces
- Data Annotations
- Spatial Types
- HierarchyId
- RowVersion and TimeStamp columns
- Lazy Loading
- Stored proc result sets
- Custom File-Based Templates
- Extra entities via partial classes
- INotifyPropertyChanged
- Syntax colour for T4