NHibernate second-level cache region defaults to entity name when region name omitted
0 reputation · 18 Jan 2022, 19:18 UTC
0 reputation · 18 Jan 2022, 19:18 UTC
When configuring NHibernate's second‑level cache, the goal is to guarantee that each entity (or collection) uses a distinct cache region so that eviction and expiration policies apply only to the intended data.
If the region attribute is omitted in the <cache> mapping or the programmatic equivalent, NHibernate treats the region as the default region named after the entity's fully qualified name. This fallback can cause unrelated entities to share the same region when their names coincide or when collections inherit the owning entity's region, leading to unexpected cache hits/misses and reduced caching efficiency.
The uncertainty lies in detecting whether any mapping has left the region name unspecified and in enforcing an explicit region name across all cached entities without manual inspection.
35275 reputation · 18 Jan 2022, 23:55 UTC
When the region attribute is omitted in an NHibernate <cache> mapping, NHibernate defaults the region name to the entity's fully qualified class name. For collection mappings, the default is the property path (role) of the collection.
This fallback is handled by the internal CacheRegionBuilder. If the mapping provides a null or empty region string, the framework ensures a unique region per type by using the entity name. While this prevents different entities from colliding in the same region by default, it prevents the application of shared expiration policies across related entities unless they are explicitly grouped into a named region.
Since NHibernate does not provide a configuration toggle to forbid default regions, you must verify the ISessionFactory metadata after initialization. The following approach iterates through mapped classes to detect fallback names:
var factory = cfg.BuildSessionFactory();
foreach (var metadata in factory.ClassMetadata.Values)
{
if (metadata.HasCache)
{
var region = metadata.GetCacheRegionName();
// Check if the region is the default (fully qualified name)
if (string.IsNullOrWhiteSpace(region) || region == metadata.EntityName)
{
throw new InvalidOperationException($"Entity {metadata.EntityName} is using the default cache region.");
}
}
}To prevent the default fallback and ensure explicit naming, use these strategies based on your mapping style:
<cache usage="read-write" region="UserRegion" />.Cache.Region("UserRegion").cache(c => c.Region("UserRegion")).To force a failure at runtime when a region is omitted, implement a custom ICacheRegionProvider. By wrapping the standard provider, you can intercept region requests and throw an exception if the requested region name matches a known entity name pattern.
Are you using a custom ICacheRegionProvider or a specific INamingStrategy that might be overriding these default naming conventions?
Use comments to ask for clarification. Post a solution as an answer.
6,600 reputation · 19 Jan 2022, 02:38 UTC
When NHibernate builds a default region name it uses the entity's full .NET type name (namespace + class) but does **not** include the assembly.
Consequently, two different assemblies that contain a type with the same fully‑qualified name (e.g., MyApp.Domain.User) will share the same cache region unless you explicitly set a region or add a prefix that distinguishes the assemblies.
You can make the default region assembly‑aware by setting the global property hibernate.cache.region_prefix (or via Configuration.SetProperty) to a value that incorporates the assembly name, for example:
var cfg = new Configuration();
cfg.SetProperty(Environment.CacheRegionPrefix, typeof(User).Assembly.GetName().Name);
// or a static prefix like "MyApp_v1_"
cfg.BuildSessionFactory();After setting this prefix, any entity or collection that omits the region attribute will receive a region like MyApp_v1_MyApp.Domain.User, preventing accidental sharing across assemblies while still allowing you to keep the mapping concise.