dotnet/efcore · error · InvalidOperationException

Property '{property}' on entity type '{entityType}' is mappe

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

With UseChangeTrackingProxies enabled, a navigation that is not a shadow property but has no PropertyInfo (navigationBase.PropertyInfo == null) throws FieldProperty (ProxyBindingRewriter.cs:84-88). Change-tracking proxies must override the property setter, so a field-only navigation without a CLR property cannot be proxied.

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 dbf9771522)

Solutions

  1. Expose a CLR property (with virtual setter) for the navigation instead of a field-only mapping.
  2. Do not use field-only access mode for navigations when change-tracking proxies are enabled.
  3. Stop using change-tracking proxies if field-only navigation mapping is required.

Example fix

// before
private List<Post> _posts = new();
public IEnumerable<Post> Posts => _posts; // field-only, no setter property
// after
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
Defensive patterns

Strategy: validation

Validate before calling

// Verify each navigation has a CLR property before enabling change-tracking proxies:
foreach (var et in modelBuilder.Model.GetEntityTypes())
foreach (var nav in et.GetNavigations())
{
    if (!nav.IsShadowProperty() && nav.PropertyInfo is null)
        throw new InvalidOperationException($"{et.Name}.{nav.Name} has no CLR property; cannot use change-tracking proxies.");
}

Prevention

When it happens

Trigger: Enabling UseChangeTrackingProxies with a navigation mapped without a CLR property (field-only access or a navigation exposed only via a field/getter).

Common situations: Using UsePropertyAccessMode.Field or field-only navigation conventions together with change-tracking proxies.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/fa2d6159910ad45f. Report an issue: GitHub.