dotnet/efcore · error · InvalidOperationException

Entity type '{entityType}' is sealed. 'UseChangeTrackingProx

Error message

Entity type '{entityType}' is sealed. '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

ProxyBindingRewriter throws ItsASeal when an entity type's CLR type is sealed and proxies are enabled (ProxyBindingRewriter.cs:72-75). Dynamic proxy generation (subclassing) cannot subclass a sealed type, so lazy-loading or change-tracking proxies are impossible for it.

Source

Thrown at src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs:74

        IConventionContext<IConventionModelBuilder> context)
    {
        if (_options?.UseProxies == true)
        {
            modelBuilder.HasAnnotation(ProxyAnnotationNames.LazyLoading, _options.UseLazyLoadingProxies);
            modelBuilder.HasAnnotation(ProxyAnnotationNames.ChangeTracking, _options.UseChangeTrackingProxies);
            modelBuilder.HasAnnotation(ProxyAnnotationNames.CheckEquality, _options.CheckEquality);

            foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
            {
                var clrType = entityType.ClrType;
                if (clrType.IsAbstract)
                {
                    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(

View on GitHub (pinned to dbf9771522)

Solutions

  1. Remove the 'sealed' modifier from the entity class so a proxy subclass can be generated.
  2. Stop using proxies for that entity type (drop UseChangeTrackingProxies/UseLazyLoadingProxies).

Example fix

// before
public sealed class Blog { public int Id { get; set; } }
// after
public class Blog { public int Id { get; set; }
Defensive patterns

Strategy: validation

Validate before calling

// Reject sealed entity types before enabling proxies:
static bool CanProxy(Type t) => !t.IsSealed && t.IsClass && !t.IsAbstract;
foreach (var et in modelBuilder.Model.GetEntityTypes())
{
    if (!CanProxy(et.ClrType))
        throw new InvalidOperationException($"{et.ClrType} must not be sealed to use proxies.");
}

Type guard

static bool IsProxyable(Type t) => t.IsClass && !t.IsAbstract && !t.IsSealed;

Prevention

When it happens

Trigger: Enabling UseChangeTrackingProxies or UseLazyLoadingProxies when any non-abstract entity type is declared 'sealed'.

Common situations: Applying proxy options to an existing model whose entity classes are sealed, or a code generator/source generator emitting sealed entities.

Related errors


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