1+ using System ;
2+ using System . Collections . Concurrent ;
13using System . Collections . Generic ;
24using NHibernate . Cache ;
35
@@ -9,17 +11,14 @@ namespace NHibernate.Caches.SysCache2
911 public class SysCacheProvider : ICacheProvider
1012 {
1113 /// <summary>pre configured cache region settings</summary>
12- private static readonly Dictionary < string , SysCacheRegion > CacheRegions ;
14+ private static readonly ConcurrentDictionary < string , Lazy < ICache > > CacheRegions = new ConcurrentDictionary < string , Lazy < ICache > > ( ) ;
1315
1416 /// <summary>list of pre configured already built cache regions</summary>
15- private static readonly CacheRegionCollection CacheRegionSettingsList ;
17+ private static readonly Dictionary < string , CacheRegionElement > CacheRegionSettings ;
1618
1719 /// <summary>log4net logger</summary>
1820 private static readonly IInternalLogger Log ;
1921
20- /// <summary>synchronizing object for the cache regions dictionary</summary>
21- private static readonly object RegionsSyncRoot = new object ( ) ;
22-
2322 /// <summary>
2423 /// Initializes the <see cref="SysCacheProvider"/> class.
2524 /// </summary>
@@ -35,12 +34,15 @@ static SysCacheProvider()
3534
3635 if ( configSection != null && configSection . CacheRegions . Count > 0 )
3736 {
38- CacheRegionSettingsList = configSection . CacheRegions ;
39- CacheRegions = new Dictionary < string , SysCacheRegion > ( CacheRegionSettingsList . Count ) ;
37+ CacheRegionSettings = new Dictionary < string , CacheRegionElement > ( configSection . CacheRegions . Count ) ;
38+ foreach ( var cacheRegion in configSection . CacheRegions )
39+ {
40+ if ( cacheRegion is CacheRegionElement element )
41+ CacheRegionSettings . Add ( element . Name , element ) ;
42+ }
4043 }
4144 else
4245 {
43- CacheRegions = new Dictionary < string , SysCacheRegion > ( 0 ) ;
4446 Log . Info (
4547 "No cache regions specified. Cache regions can be specified in sysCache configuration section with custom settings." ) ;
4648 }
@@ -56,49 +58,36 @@ public ICache BuildCache(string regionName, IDictionary<string, string> properti
5658 // since query caches are not configured at session factory startup. This may also happen
5759 // if many session factories are built.
5860 // This cache avoids to duplicate the configured SQL dependencies registration in above cases.
59- if ( ! string . IsNullOrEmpty ( regionName ) && CacheRegions . TryGetValue ( regionName , out var cache ) )
61+ if ( ! string . IsNullOrEmpty ( regionName )
62+ // We do not cache non-configured caches, so must first look-up settings for knowing if it
63+ // is a configured one.
64+ && CacheRegionSettings . TryGetValue ( regionName , out var regionSettings ) )
6065 {
61- return cache ;
66+ // The Lazy<T> is required for ensuring the cache is built only once. ConcurrentDictionary
67+ // may run concurrently the value factory for the same key, but it will yield only one
68+ // of the resulting Lazy<T>. The lazy will then actually build the cache when accessing its
69+ // value after having obtained it, and it will not do that concurrently.
70+ // https://stackoverflow.com/a/31637510/1178314
71+ var cache = CacheRegions . GetOrAdd ( regionName ,
72+ r => new Lazy < ICache > ( ( ) => BuildCache ( r , properties , regionSettings ) ) ) ;
73+ return cache . Value ;
6274 }
6375
64- // Build the cache from preconfigured values if the region has configuration values
65- if ( CacheRegionSettingsList != null )
66- {
67- var regionSettings = regionName == null ? null : CacheRegionSettingsList [ regionName ] ;
68-
69- if ( regionSettings != null )
70- {
71- SysCacheRegion cacheRegion ;
72-
73- lock ( RegionsSyncRoot )
74- {
75- // Note that the only reason we have to do this double check is because the query cache
76- // can try to create caches at unpredictable times.
77- if ( CacheRegions . TryGetValue ( regionName , out cacheRegion ) == false )
78- {
79- if ( Log . IsDebugEnabled )
80- {
81- Log . DebugFormat ( "building cache region, '{0}', from configuration" , regionName ) ;
82- }
83-
84- //build the cache region with settings and put it into the list so that this proces will not occur again
85- cacheRegion = new SysCacheRegion ( regionName , regionSettings , properties ) ;
86- CacheRegions [ regionName ] = cacheRegion ;
87- }
88- }
89-
90- return cacheRegion ;
91- }
92- }
76+ // We will end up creating cache regions here for cache regions that NHibernate
77+ // uses internally and cache regions that weren't specified in the application config file
78+ return BuildCache ( regionName , properties , null ) ;
79+ }
9380
81+ private ICache BuildCache ( string regionName , IDictionary < string , string > properties , CacheRegionElement settings )
82+ {
9483 if ( Log . IsDebugEnabled )
9584 {
96- Log . DebugFormat ( "building non-configured cache region : {0}" , regionName ) ;
85+ Log . DebugFormat (
86+ settings != null
87+ ? "building cache region, '{0}', from configuration"
88+ : "building non-configured cache region : {0}" , regionName ) ;
9789 }
98-
99- //we will end up creating cache regions here for cache regions that nhibernate
100- //uses internally and cache regions that weren't specified in the application config file
101- return new SysCacheRegion ( regionName , properties ) ;
90+ return new SysCacheRegion ( regionName , settings , properties ) ;
10291 }
10392
10493 /// <inheritdoc />
0 commit comments