dotnet/efcore · error · InvalidOperationException
The type '{dictionaryType}' used for shared entity type '{en
Error message
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. What it means
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.
Source
Thrown at src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs:173
var indexerChecked = false;
foreach (var property in entityType.GetDeclaredProperties()
.Where(p => !p.IsShadowProperty()))
{
if (property.IsIndexerProperty())
{
if (!indexerChecked)
{
indexerChecked = true;
if (!property.PropertyInfo!.SetMethod!.IsReallyVirtual())
{
if (clrType.IsGenericType
&& clrType.GetGenericTypeDefinition() == typeof(Dictionary<,>)
&& clrType.GenericTypeArguments[0] == typeof(string))
{
if (entityType.GetProperties().Any(p => !p.IsPrimaryKey()))
{
throw new InvalidOperationException(
ProxiesStrings.DictionaryCannotBeProxied(
clrType.ShortDisplayName(),
entityType.DisplayName(),
typeof(IDictionary<,>).MakeGenericType(clrType.GenericTypeArguments)
.ShortDisplayName()));
}
}
else
{
throw new InvalidOperationException(
ProxiesStrings.NonVirtualIndexerProperty(entityType.DisplayName()));
}
}
}
}
else
{
if (property.PropertyInfo == null)View on GitHub (pinned to dbf9771522)
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.
Example fix
// before
// shared entity CLR type is Dictionary<string, object>
modelBuilder.SharedTypeEntity<Dictionary<string, object>>("Dyn");
options.UseChangeTrackingProxies();
// after
public class DynDict : Dictionary<string, object>
{
public virtual new object? this[string key]
{ get => base[key]; set => base[key] = value; }
}
// use DynDict as the shared entity CLR type Defensive patterns
Strategy: validation
Validate before calling
// Avoid change-tracking proxies on Dictionary-backed shared types with non-key properties,
// or use a custom IDictionary implementation with a virtual indexer:
if (clrType.IsGenericType
&& clrType.GetGenericTypeDefinition() == typeof(Dictionary<,>)
&& clrType.GenericTypeArguments[0] == typeof(string))
throw new InvalidOperationException("Dictionary shared types cannot be change-track-proxied; use a virtual-indexer IDictionary impl."); Type guard
static bool IsProxyableDictionary(Type t)
=> t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>))
&& t.GetProperty("Item")?.SetMethod?.IsVirtual == true; Prevention
- 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.
When it happens
Trigger: Enabling UseChangeTrackingProxies with a shared entity type (keyless/queryable property) backed by Dictionary<string,TValue> that has properties beyond the primary key.
Common situations: Using shared-type entities (e.g. for JSON/dynamic columns or queryable properties) with change-tracking proxies enabled.
Related errors
- The mapped indexer property on entity type '{entityType}' is
- Entity type '{entityType}' is sealed. 'UseChangeTrackingProx
- Property '{property}' on entity type '{entityType}' is mappe
- Property '{entityType}.{property}' is not virtual. 'UseChang
- 'UseChangeTrackingProxies' and 'UseLazyLoadingProxies' each
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/c9850d6025acb7e2.
Report an issue: GitHub.