{"record":{"id":"4e84ed32547c6942","repo":"dotnet/efcore","slug":"entity-type-entitytype-is-sealed-usechangetr","errorCode":null,"errorMessage":"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.","messagePattern":"Entity type '(.+?)' 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\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs","lineNumber":74,"sourceCode":"        IConventionContext<IConventionModelBuilder> context)\n    {\n        if (_options?.UseProxies == true)\n        {\n            modelBuilder.HasAnnotation(ProxyAnnotationNames.LazyLoading, _options.UseLazyLoadingProxies);\n            modelBuilder.HasAnnotation(ProxyAnnotationNames.ChangeTracking, _options.UseChangeTrackingProxies);\n            modelBuilder.HasAnnotation(ProxyAnnotationNames.CheckEquality, _options.CheckEquality);\n\n            foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())\n            {\n                var clrType = entityType.ClrType;\n                if (clrType.IsAbstract)\n                {\n                    continue;\n                }\n\n                if (clrType.IsSealed)\n                {\n                    throw new InvalidOperationException(ProxiesStrings.ItsASeal(entityType.DisplayName()));\n                }\n\n                foreach (var navigationBase in entityType.GetDeclaredNavigations()\n                             .Concat<IConventionNavigationBase>(entityType.GetDeclaredSkipNavigations()))\n                {\n                    if (!navigationBase.IsShadowProperty())\n                    {\n                        if (_options.UseChangeTrackingProxies)\n                        {\n                            if (navigationBase.PropertyInfo == null)\n                            {\n                                throw new InvalidOperationException(\n                                    ProxiesStrings.FieldProperty(navigationBase.Name, entityType.DisplayName()));\n                            }\n\n                            if (navigationBase.PropertyInfo.SetMethod?.IsReallyVirtual() == false)\n                            {\n                                throw new InvalidOperationException(","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs#L56-L92","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\npublic sealed class Blog\n{\n    public int Id { get; set; }\n    public virtual List<Post> Posts { get; set; }\n}\n// UseChangeTrackingProxies() → throws ItsASeal\n\n// after\npublic class Blog\n{\n    public int Id { get; set; }\n    public virtual List<Post> Posts { get; set; }\n}","handlingStrategy":"validation","validationCode":"// At startup, scan all entity CLR types in the model for sealed classes.\nstatic void ValidateNoSealedEntities(ModelBuilder modelBuilder)\n{\n    foreach (var entityType in modelBuilder.Model.GetEntityTypes())\n    {\n        if (entityType.ClrType.IsSealed)\n        {\n            throw new InvalidOperationException(\n                $\"{entityType.ClrType.Name} is sealed. Remove 'sealed' to use proxies.\");\n        }\n    }\n}","typeGuard":"// Type guard: check if a CLR type is suitable for proxy generation\nstatic bool IsProxyCompatible(Type clrType)\n    => !clrType.IsSealed && clrType.IsPublic && !clrType.IsAbstract;","tryCatchPattern":"try\n{\n    using var context = new MyContext(options);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"sealed\"))\n{\n    // Remove sealed from the offending entity class, then retry\n    logger.LogError(ex, \"An entity type is sealed; proxies require unsealed types.\");\n    throw;\n}","preventionTips":["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."],"tags":["efcore","proxies","sealed","entity-configuration","model-building"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}