dotnet/efcore · error · InvalidOperationException
Entity type ' ' is sealed. 'UseChangeTrackingProxies'…
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 iterates every concrete (non-abstract) entity type in the model. If an entity's CLR type is sealed (clrType.IsSealed), it throws ItsASeal because Castle DynamicProxy cannot create a proxy subclass of a sealed class. Change-tracking proxies need to override all properties; lazy-loading proxies need to override navigation properties — both impossible when the class cannot be inherited.
Solutions
- Remove the sealed modifier from every entity class that participates in the DbContext model.
- If the entity must stay sealed, exclude it from proxy generation or do not enable proxies.
- Ensure all entity types are public, unsealed, and have virtual properties plus a public/protected constructor.
Example fix
// before
public sealed class Blog
{
public int Id { get; set; }
public virtual List<Post> Posts { get; set; }
}
// UseChangeTrackingProxies() → throws ItsASeal
// after
public class Blog
{
public int Id { get; set; }
public virtual List<Post> Posts { get; set; }
} Defensive patterns
Strategy: validation
Validate before calling
// At startup, scan all entity CLR types in the model for sealed classes.
static void ValidateNoSealedEntities(ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
if (entityType.ClrType.IsSealed)
{
throw new InvalidOperationException(
$"{entityType.ClrType.Name} is sealed. Remove 'sealed' to use proxies.");
}
}
} Type guard
// Type guard: check if a CLR type is suitable for proxy generation
static bool IsProxyCompatible(Type clrType)
=> !clrType.IsSealed && clrType.IsPublic && !clrType.IsAbstract; Try / catch
try
{
using var context = new MyContext(options);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("sealed"))
{
// Remove sealed from the offending entity class, then retry
logger.LogError(ex, "An entity type is sealed; proxies require unsealed types.");
throw;
} Prevention
- Do not seal entity classes that participate in a DbContext model when using proxies.
- Add a startup model scan that checks all entity CLR types for sealed/public/abstract.
- Ensure entity types are public and unsealed by convention in your project guidelines.
- Review generated/scaffolded entity classes for sealed modifiers before enabling proxies.
When it happens
Trigger: Enabling UseChangeTrackingProxies() or UseLazyLoadingProxies() when at least one entity type in the model is declared as a sealed class. The throw occurs during model finalization when ProxyBindingRewiter processes the entity types.
Common situations: Using DTOs or domain entities declared as sealed (common in some C# coding conventions that seal by default). Adding proxies to an existing model that contains sealed entities. Code generation tools that emit sealed entity classes.
Related errors
- Property ' . ' is not virtual. 'UseChangeTrackingProxies'…
- Property ' ' on entity type ' ' is mapped without a CLR…
- The mapped indexer property on entity type
- The type ' ' used for shared entity type ' ' is not…
- 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/4e84ed32547c6942.
Report an issue: GitHub.
Appendix: 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 3a2006ef56)