{"record":{"id":"e811946a5f8b191f","repo":"dotnet/aspnetcore","slug":"the-type-targettype-fullname-declares-a-proper","errorCode":null,"errorMessage":"The type '{targetType.FullName}' declares a property matching the name '{propertyName}' that is not public. Persistent service properties must be public.","messagePattern":"The type '(.+?)' declares a property matching the name '(.+?)' that is not public\\. Persistent service properties must be public\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Components/Components/src/PersistentState/PersistentServicesRegistry.cs","lineNumber":191,"sourceCode":"                foreach (var attribute in propertyInfo.GetCustomAttributes())\n                {\n                    if (attribute is PersistentStateAttribute persistentStateAttribute)\n                    {\n                        parameterAttribute = persistentStateAttribute;\n                        break;\n                    }\n                }\n                if (parameterAttribute == null)\n                {\n                    continue;\n                }\n\n                var propertyName = propertyInfo.Name;\n                var key = ComputeKey(keyType, propertyName);\n                keys.Add(new(key, propertyInfo.PropertyType));\n                if (propertyInfo.SetMethod == null || !propertyInfo.SetMethod.IsPublic)\n                {\n                    throw new InvalidOperationException(\n                        $\"The type '{targetType.FullName}' declares a property matching the name '{propertyName}' that is not public. Persistent service properties must be public.\");\n                }\n\n                if (propertyInfo.GetMethod == null || !propertyInfo.GetMethod.IsPublic)\n                {\n                    throw new InvalidOperationException(\n                        $\"The type '{targetType.FullName}' declares a property matching the name '{propertyName}' that is not public. Persistent service properties must be public.\");\n                }\n\n                var restoreOptions = new RestoreOptions\n                {\n                    RestoreBehavior = parameterAttribute.RestoreBehavior,\n                    AllowUpdates = parameterAttribute.AllowUpdates,\n                };\n\n                var propertySetter = new PropertySetter(targetType, propertyInfo);\n                var propertyGetter = new PropertyGetter(targetType, propertyInfo);\n","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Components/src/PersistentState/PersistentServicesRegistry.cs#L173-L209","documentation":"PersistentServicesRegistry.PropertiesAccessor scans a persistent service type for properties annotated with [PersistentState]. For each such property it requires a public setter so that restoration can write the deserialized value back. If propertyInfo.SetMethod is null (no setter) or not public (private/protected/internal setter), restoration cannot inject the value and the registry throws during service registration.","triggerScenarios":"Declaring a property with [PersistentState] that has a private or protected setter, an init-only setter that is non-public, or no setter at all (read-only/auto-get-only). Registering such a type via PersistentServicesRegistry causes the PropertiesAccessor constructor to throw on first restoration.","commonSituations":"Encapsulating persistent service state with a private setter to enforce invariants (common DDD pattern) but still wanting it restored. Using { get; init; } without making the init accessor public. Migrating a service to persistent-state attributes and forgetting to expose the setter.","solutions":["Make the setter public: change 'private set' to 'public set' on the [PersistentState] property.","For init-only patterns, use 'public init' so the property is publicly settable during restoration.","If the property must stay encapsulated, introduce a separate public property backed by the persistent state, or apply [PersistentState] to a public property that delegates to the encapsulated field."],"exampleFix":"// before\n[PersistentState] public int Count { get; private set; }\n\n// after\n[PersistentState] public int Count { get; set; }","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static bool HasPublicSetter(PropertyInfo p)\n    => p.SetMethod != null && p.SetMethod.IsPublic;\n\n// In a unit test for persistent service types:\nforeach (var p in typeof(MyService).GetProperties())\n{\n    if (p.GetCustomAttribute<PersistentStateAttribute>() != null)\n        Assert.True(HasPublicSetter(p));\n}","tryCatchPattern":null,"preventionTips":["When applying [PersistentState], make both getter and setter public.","Add a unit test asserting all [PersistentState] properties have public accessors.","Avoid private setters on persistent service state; validate in OnInitialized instead."],"tags":["blazor","persistent-state","reflection","services"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}