1
+ using System ;
2
+ using System . Collections . Concurrent ;
1
3
using System . Collections . Generic ;
2
4
using NHibernate . Cache ;
3
5
@@ -9,17 +11,14 @@ namespace NHibernate.Caches.SysCache2
9
11
public class SysCacheProvider : ICacheProvider
10
12
{
11
13
/// <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 > > ( ) ;
13
15
14
16
/// <summary>list of pre configured already built cache regions</summary>
15
- private static readonly CacheRegionCollection CacheRegionSettingsList ;
17
+ private static readonly Dictionary < string , CacheRegionElement > CacheRegionSettings ;
16
18
17
19
/// <summary>log4net logger</summary>
18
20
private static readonly IInternalLogger Log ;
19
21
20
- /// <summary>synchronizing object for the cache regions dictionary</summary>
21
- private static readonly object RegionsSyncRoot = new object ( ) ;
22
-
23
22
/// <summary>
24
23
/// Initializes the <see cref="SysCacheProvider"/> class.
25
24
/// </summary>
@@ -35,12 +34,15 @@ static SysCacheProvider()
35
34
36
35
if ( configSection != null && configSection . CacheRegions . Count > 0 )
37
36
{
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
+ }
40
43
}
41
44
else
42
45
{
43
- CacheRegions = new Dictionary < string , SysCacheRegion > ( 0 ) ;
44
46
Log . Info (
45
47
"No cache regions specified. Cache regions can be specified in sysCache configuration section with custom settings." ) ;
46
48
}
@@ -56,49 +58,36 @@ public ICache BuildCache(string regionName, IDictionary<string, string> properti
56
58
// since query caches are not configured at session factory startup. This may also happen
57
59
// if many session factories are built.
58
60
// 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 ) )
60
65
{
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 ;
62
74
}
63
75
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
+ }
93
80
81
+ private ICache BuildCache ( string regionName , IDictionary < string , string > properties , CacheRegionElement settings )
82
+ {
94
83
if ( Log . IsDebugEnabled )
95
84
{
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 ) ;
97
89
}
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 ) ;
102
91
}
103
92
104
93
/// <inheritdoc />
0 commit comments