dotnet/efcore · error · InvalidOperationException
Property ' ' on entity type ' ' is mapped without a CLR…
Error message
Property '{property}' on entity type '{entityType}' is mapped without a CLR property. 'UseChangeTrackingProxies' requires all entity types to be public, unsealed, have virtual properties, and have a public or protected constructor. 'UseLazyLoadingProxies' requires only the navigation properties be virtual. What it means
When UseChangeTrackingProxies is enabled, ProxyBindingRewriter examines each declared navigation and skip-navigation. For change-tracking proxies, if a navigation has no CLR PropertyInfo (navigationBase.PropertyInfo == null — meaning it is a field-only or shadow navigation), it throws FieldProperty. Change-tracking proxies override property getters/setters to intercept changes, which is impossible without a CLR property to override.
Solutions
- Add a public or protected virtual CLR property for every navigation that currently has only a field.
- Switch to UseLazyLoadingProxies (which can tolerate field access via SetPropertyAccessMode) if full change-tracking proxies are not needed.
- Do not enable UseChangeTrackingProxies if your model intentionally uses field-only navigations.
Example fix
// before — field-only navigation, no CLR property
public class Blog
{
[BackingField("_posts")]
public ICollection<Post> Posts => _posts;
private readonly List<Post> _posts = new();
}
// UseChangeTrackingProxies() → throws FieldProperty
// after — add a settable virtual CLR property
public class Blog
{
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
} Defensive patterns
Strategy: validation
Validate before calling
// At startup, verify all navigations have CLR properties when change-tracking proxies are enabled.
static void ValidateNavigationProperties(ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var nav in entityType.GetDeclaredNavigations()
.Concat<INavigationBase>(entityType.GetDeclaredSkipNavigations()))
{
if (!nav.IsShadowProperty() && nav.PropertyInfo == null)
{
throw new InvalidOperationException(
$"{entityType.DisplayName()}.{nav.Name} has no CLR property; " +
"change-tracking proxies require one.");
}
}
}
} Try / catch
try
{
using var context = new MyContext(options);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("mapped without a CLR property"))
{
// Add a CLR property for the navigation, or switch to lazy-loading proxies
logger.LogError(ex, "Navigation lacks CLR property; change-tracking proxies need one.");
throw;
} Prevention
- Ensure every navigation has a public or protected virtual CLR property (not field-only).
- Avoid [BackingField]-only navigations when using change-tracking proxies.
- Validate entity metadata at startup to catch field-only navigations early.
- Consider lazy-loading proxies or explicit loading if field-only navigations are required.
When it happens
Trigger: Enabling UseChangeTrackingProxies() when at least one navigation property is mapped without a CLR property — e.g. using [BackingField] with only a field and no property, or configuring a shadow navigation in OnModelCreating. The throw occurs during model finalization.
Common situations: Domain models that use field-only collections (e.g. private readonly List<Post> _posts with [BackingField]) for encapsulation. Configuring navigations entirely in fluent API without corresponding CLR properties. Combining DDD-style entity patterns with change-tracking proxies.
Related errors
- Property ' . ' is not virtual. 'UseChangeTrackingProxies'…
- The mapped indexer property on entity type
- The type ' ' used for shared entity type ' ' is not…
- Entity type ' ' is sealed. 'UseChangeTrackingProxies'…
- The entity type ' ' was not found. Ensure that the entity…
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/fa2d6159910ad45f.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs:86
{
continue;
}
if (clrType.IsSealed)
{
throw new InvalidOperationException(ProxiesStrings.ItsASeal(entityType.DisplayName()));
}
foreach (var navigationBase in entityType.GetDeclaredNavigations()
.Concat<IConventionNavigationBase>(entityType.GetDeclaredSkipNavigations()))
{
if (!navigationBase.IsShadowProperty())
{
if (_options.UseChangeTrackingProxies)
{
if (navigationBase.PropertyInfo == null)
{
throw new InvalidOperationException(
ProxiesStrings.FieldProperty(navigationBase.Name, entityType.DisplayName()));
}
if (navigationBase.PropertyInfo.SetMethod?.IsReallyVirtual() == false)
{
throw new InvalidOperationException(
ProxiesStrings.NonVirtualProperty(navigationBase.Name, entityType.DisplayName()));
}
}
if (_options.UseLazyLoadingProxies
&& navigationBase.LazyLoadingEnabled)
{
if (navigationBase.PropertyInfo == null
|| !navigationBase.PropertyInfo.GetMethod!.IsReallyVirtual())
{
if (!_options.IgnoreNonVirtualNavigations
&& navigationBase is not INavigation { ForeignKey.IsOwnership: true })View on GitHub (pinned to 3a2006ef56)