{"record":{"id":"c9850d6025acb7e2","repo":"dotnet/efcore","slug":"the-type-dictionarytype-used-for-shared-entity","errorCode":null,"errorMessage":"The type '{dictionaryType}' used for shared entity type '{entityType}' is not suitable for use as a change-tracking proxy because its indexer property is not virtual. Consider using an implementation of '{interfaceType}' that allows overriding of the indexer.","messagePattern":"The type '(.+?)' used for shared entity type '(.+?)' is not suitable for use as a change-tracking proxy because its indexer property is not virtual\\. Consider using an implementation of '(.+?)' that allows overriding of the indexer\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs","lineNumber":173,"sourceCode":"                    var indexerChecked = false;\n                    foreach (var property in entityType.GetDeclaredProperties()\n                                 .Where(p => !p.IsShadowProperty()))\n                    {\n                        if (property.IsIndexerProperty())\n                        {\n                            if (!indexerChecked)\n                            {\n                                indexerChecked = true;\n\n                                if (!property.PropertyInfo!.SetMethod!.IsReallyVirtual())\n                                {\n                                    if (clrType.IsGenericType\n                                        && clrType.GetGenericTypeDefinition() == typeof(Dictionary<,>)\n                                        && clrType.GenericTypeArguments[0] == typeof(string))\n                                    {\n                                        if (entityType.GetProperties().Any(p => !p.IsPrimaryKey()))\n                                        {\n                                            throw new InvalidOperationException(\n                                                ProxiesStrings.DictionaryCannotBeProxied(\n                                                    clrType.ShortDisplayName(),\n                                                    entityType.DisplayName(),\n                                                    typeof(IDictionary<,>).MakeGenericType(clrType.GenericTypeArguments)\n                                                        .ShortDisplayName()));\n                                        }\n                                    }\n                                    else\n                                    {\n                                        throw new InvalidOperationException(\n                                            ProxiesStrings.NonVirtualIndexerProperty(entityType.DisplayName()));\n                                    }\n                                }\n                            }\n                        }\n                        else\n                        {\n                            if (property.PropertyInfo == null)","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs#L155-L191","documentation":"With UseChangeTrackingProxies enabled, a shared-type entity whose CLR type is Dictionary<string,TValue> has a non-virtual indexer; since there are non-primary-key properties the proxy must intercept the indexer, but Dictionary's indexer cannot be overridden, so DictionaryCannotBeProxied is thrown (ProxyBindingRewriter.cs:167-179). It suggests using an IDictionary<,> implementation that allows overriding the indexer.","triggerScenarios":"Enabling UseChangeTrackingProxies with a shared entity type (keyless/queryable property) backed by Dictionary<string,TValue> that has properties beyond the primary key.","commonSituations":"Using shared-type entities (e.g. for JSON/dynamic columns or queryable properties) with change-tracking proxies enabled.","solutions":["Use a custom type implementing IDictionary<string,TValue> whose indexer is virtual so it can be proxied.","Disable change-tracking proxies when using dictionary-backed shared entities.","Reduce the shared entity to key-only properties so no indexer interception is required."],"exampleFix":"// before\n// shared entity CLR type is Dictionary<string, object>\nmodelBuilder.SharedTypeEntity<Dictionary<string, object>>(\"Dyn\");\noptions.UseChangeTrackingProxies();\n// after\npublic class DynDict : Dictionary<string, object>\n{\n    public virtual new object? this[string key]\n    { get => base[key]; set => base[key] = value; }\n}\n// use DynDict as the shared entity CLR type","handlingStrategy":"validation","validationCode":"// Avoid change-tracking proxies on Dictionary-backed shared types with non-key properties,\n// or use a custom IDictionary implementation with a virtual indexer:\nif (clrType.IsGenericType\n    && clrType.GetGenericTypeDefinition() == typeof(Dictionary<,>)\n    && clrType.GenericTypeArguments[0] == typeof(string))\n    throw new InvalidOperationException(\"Dictionary shared types cannot be change-track-proxied; use a virtual-indexer IDictionary impl.\");","typeGuard":"static bool IsProxyableDictionary(Type t)\n    => t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>))\n       && t.GetProperty(\"Item\")?.SetMethod?.IsVirtual == true;","tryCatchPattern":null,"preventionTips":["Do not enable change-tracking proxies for Dictionary<string,>-backed shared entities with non-key properties.","Provide a custom IDictionary<string,TValue> implementation with a virtual indexer for proxying.","Prefer not using proxies with shared-type/dynamic entities."],"tags":["ef-core","proxies","change-tracking-proxies","shared-type","indexer","model-design"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}