Skip to content

Commit 01c3f97

Browse files
Refine .Net Core distributed cache factory model
And provide by default the MemoryDistributedCacheFactory. To be squashed.
1 parent 9c70599 commit 01c3f97

File tree

8 files changed

+237
-93
lines changed

8 files changed

+237
-93
lines changed

CoreDistributedCache/NHibernate.Caches.CoreDistributedCache.Tests/App.config

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
77
</configSections>
88

9-
<coredistributedcache factory-class="NHibernate.Caches.CoreDistributedCache.Tests.MemoryDistributedCacheFactory,NHibernate.Caches.CoreDistributedCache.Tests">
9+
<coredistributedcache factory-class="NHibernate.Caches.CoreDistributedCache.MemoryDistributedCacheFactory,NHibernate.Caches.CoreDistributedCache">
10+
<properties>
11+
<property name="expiration-scan-frequency">00:10:00</property>
12+
<property name="size-limit">1048576</property>
13+
</properties>
1014
<cache region="foo" expiration="500" sliding="true" />
1115
<cache region="noExplicitExpiration" sliding="true" />
1216
</coredistributedcache>

CoreDistributedCache/NHibernate.Caches.CoreDistributedCache.Tests/CoreDistributedCacheProviderFixture.cs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222

2323
using System;
2424
using System.Collections.Generic;
25+
using System.Reflection;
26+
using Microsoft.Extensions.Caching.Distributed;
27+
using Microsoft.Extensions.Caching.Memory;
2528
using NHibernate.Cache;
2629
using NHibernate.Caches.Common.Tests;
2730
using NUnit.Framework;
@@ -34,6 +37,36 @@ public class CoreDistributedCacheProviderFixture : CacheProviderFixture
3437
protected override Func<ICacheProvider> ProviderBuilder =>
3538
() => new CoreDistributedCacheProvider();
3639

40+
private static readonly FieldInfo MemoryCacheField =
41+
typeof(MemoryDistributedCache).GetField("_memCache", BindingFlags.Instance | BindingFlags.NonPublic);
42+
43+
private static readonly FieldInfo CacheOptionsField =
44+
typeof(MemoryCache).GetField("_options", BindingFlags.Instance | BindingFlags.NonPublic);
45+
46+
[Test]
47+
public void ConfiguredCacheFactory()
48+
{
49+
var factory = CoreDistributedCacheProvider.CacheFactory;
50+
Assert.That(factory, Is.Not.Null, "Factory not found");
51+
Assert.That(factory, Is.InstanceOf<MemoryDistributedCacheFactory>(), "Unexpected factory");
52+
var cache1 = factory.BuildCache();
53+
Assert.That(cache1, Is.Not.Null, "Factory has yielded null");
54+
Assert.That(cache1, Is.InstanceOf<MemoryDistributedCache>(), "Unexpected cache");
55+
var cache2 = factory.BuildCache();
56+
Assert.That(cache2, Is.EqualTo(cache1),
57+
"The distributed cache factory is supposed to always yield the same instance");
58+
59+
var memCache = MemoryCacheField.GetValue(cache1);
60+
Assert.That(memCache, Is.Not.Null, "Underlying memory cache not found");
61+
Assert.That(memCache, Is.InstanceOf<MemoryCache>(), "Unexpected memory cache");
62+
var options = CacheOptionsField.GetValue(memCache);
63+
Assert.That(options, Is.Not.Null, "Memory cache options not found");
64+
Assert.That(options, Is.InstanceOf<MemoryCacheOptions>(), "Unexpected options type");
65+
var memOptions = (MemoryCacheOptions) options;
66+
Assert.That(memOptions.ExpirationScanFrequency, Is.EqualTo(TimeSpan.FromMinutes(10)));
67+
Assert.That(memOptions.SizeLimit, Is.EqualTo(1048576));
68+
}
69+
3770
[Test]
3871
public void TestBuildCacheFromConfig()
3972
{
@@ -48,18 +81,19 @@ public void TestExpiration()
4881
Assert.That(cache, Is.Not.Null, "pre-configured foo cache not found");
4982
Assert.That(cache.Expiration, Is.EqualTo(TimeSpan.FromSeconds(500)), "Unexpected expiration value for foo region");
5083

51-
cache = (CoreDistributedCache)DefaultProvider.BuildCache("noExplicitExpiration", null);
84+
cache = (CoreDistributedCache) DefaultProvider.BuildCache("noExplicitExpiration", null);
5285
Assert.That(cache.Expiration, Is.EqualTo(TimeSpan.FromSeconds(300)),
5386
"Unexpected expiration value for noExplicitExpiration region");
5487
Assert.That(cache.UseSlidingExpiration, Is.True, "Unexpected sliding value for noExplicitExpiration region");
5588

56-
cache = (CoreDistributedCache)DefaultProvider
89+
cache = (CoreDistributedCache) DefaultProvider
5790
.BuildCache("noExplicitExpiration", new Dictionary<string, string> { { "expiration", "100" } });
5891
Assert.That(cache.Expiration, Is.EqualTo(TimeSpan.FromSeconds(100)),
5992
"Unexpected expiration value for noExplicitExpiration region with default expiration");
6093

61-
cache = (CoreDistributedCache)DefaultProvider
62-
.BuildCache("noExplicitExpiration", new Dictionary<string, string> { { Cfg.Environment.CacheDefaultExpiration, "50" } });
94+
cache = (CoreDistributedCache) DefaultProvider
95+
.BuildCache("noExplicitExpiration",
96+
new Dictionary<string, string> { { Cfg.Environment.CacheDefaultExpiration, "50" } });
6397
Assert.That(cache.Expiration, Is.EqualTo(TimeSpan.FromSeconds(50)),
6498
"Unexpected expiration value for noExplicitExpiration region with cache.default_expiration");
6599
}

CoreDistributedCache/NHibernate.Caches.CoreDistributedCache.Tests/CoreDistributedCacheSectionHandlerFixture.cs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,25 +41,41 @@ public void TestGetConfigNullSection()
4141
var handler = new CoreDistributedCacheSectionHandler();
4242
var section = new XmlDocument();
4343
var result = handler.Create(null, null, section);
44-
Assert.That(result, Is.Not.Null);
45-
Assert.IsTrue(result is CacheConfig[]);
46-
var caches = result as CacheConfig[];
47-
Assert.That(caches.Length, Is.EqualTo(0));
44+
Assert.That(result, Is.Not.Null, "result");
45+
Assert.That(result, Is.InstanceOf<CacheConfig>());
46+
var config = (CacheConfig) result;
47+
Assert.That(config.Properties, Is.Not.Null, "Properties");
48+
Assert.That(config.Properties.Count, Is.EqualTo(0), "Properties count");
49+
Assert.That(config.Regions, Is.Not.Null, "Regions");
50+
Assert.That(config.Regions.Length, Is.EqualTo(0));
4851
}
4952

5053
[Test]
5154
public void TestGetConfigFromFile()
5255
{
53-
const string xmlSimple = "<coredistributedcache><cache region=\"foo\" expiration=\"500\" sliding=\"true\" /></coredistributedcache>";
56+
const string xmlSimple = "<coredistributedcache factory-class=\"factory1\"><properties><property name=\"prop1\">Value1</property></properties><cache region=\"foo\" expiration=\"500\" sliding=\"true\" /></coredistributedcache>";
5457

5558
var handler = new CoreDistributedCacheSectionHandler();
5659
var section = GetConfigurationSection(xmlSimple);
5760
var result = handler.Create(null, null, section);
58-
Assert.That(result, Is.Not.Null);
59-
Assert.IsTrue(result is CacheConfig[]);
60-
var caches = result as CacheConfig[];
61-
Assert.That(caches.Length, Is.EqualTo(1));
62-
Assert.That(caches[0].Properties, Does.ContainKey("cache.use_sliding_expiration"));
61+
Assert.That(result, Is.Not.Null, "result");
62+
Assert.That(result, Is.InstanceOf<CacheConfig>());
63+
var config = (CacheConfig) result;
64+
65+
Assert.That(config.FactoryClass, Is.EqualTo("factory1"));
66+
67+
Assert.That(config.Properties, Is.Not.Null, "Properties");
68+
Assert.That(config.Properties.Count, Is.EqualTo(1), "Properties count");
69+
Assert.That(config.Properties, Does.ContainKey("prop1"));
70+
Assert.That(config.Properties["prop1"], Is.EqualTo("Value1"));
71+
72+
Assert.That(config.Regions, Is.Not.Null, "Regions");
73+
Assert.That(config.Regions.Length, Is.EqualTo(1), "Regions count");
74+
Assert.That(config.Regions[0].Region, Is.EqualTo("foo"));
75+
Assert.That(config.Regions[0].Properties, Does.ContainKey("cache.use_sliding_expiration"));
76+
Assert.That(config.Regions[0].Properties["cache.use_sliding_expiration"], Is.EqualTo("true"));
77+
Assert.That(config.Regions[0].Properties, Does.ContainKey("expiration"));
78+
Assert.That(config.Regions[0].Properties["expiration"], Is.EqualTo("500"));
6379
}
6480
}
6581
}

CoreDistributedCache/NHibernate.Caches.CoreDistributedCache.Tests/MemoryDistributedCacheFactory.cs

Lines changed: 0 additions & 19 deletions
This file was deleted.

CoreDistributedCache/NHibernate.Caches.CoreDistributedCache/CacheConfig.cs

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,47 @@
44
namespace NHibernate.Caches.CoreDistributedCache
55
{
66
/// <summary>
7-
/// Cache config properties.
7+
/// Cache configuration properties.
88
/// </summary>
99
public class CacheConfig
1010
{
1111
/// <summary>
12-
/// Build a cache global configuration.
12+
/// Build a cache configuration.
1313
/// </summary>
1414
/// <param name="factoryClass">The <see cref="IDistributedCacheFactory"/> factory class name to use for getting
1515
/// <see cref="IDistributedCache"/> instances.</param>
16-
public CacheConfig(string factoryClass)
16+
/// <param name="properties">The cache configuration properties.</param>
17+
/// <param name="regions">The configured cache regions.</param>
18+
public CacheConfig(string factoryClass, IDictionary<string, string> properties, RegionConfig[] regions)
1719
{
1820
FactoryClass = factoryClass;
19-
Global = true;
21+
Regions = regions;
22+
Properties = properties;
2023
}
2124

25+
/// <summary>The <see cref="IDistributedCacheFactory"/> factory class name to use for getting
26+
/// <see cref="IDistributedCache"/> instances.</summary>
27+
public string FactoryClass { get; }
28+
29+
/// <summary>The configured cache regions.</summary>
30+
public RegionConfig[] Regions { get; }
31+
32+
/// <summary>The cache configuration properties.</summary>
33+
public IDictionary<string, string> Properties { get; }
34+
}
35+
36+
/// <summary>
37+
/// Region configuration properties.
38+
/// </summary>
39+
public class RegionConfig
40+
{
2241
/// <summary>
2342
/// Build a cache region configuration.
2443
/// </summary>
2544
/// <param name="region">The configured cache region.</param>
2645
/// <param name="expiration">The expiration for the region.</param>
2746
/// <param name="sliding">Whether the expiration should be sliding or not.</param>
28-
public CacheConfig(string region, string expiration, string sliding)
47+
public RegionConfig(string region, string expiration, string sliding)
2948
{
3049
Region = region;
3150
Properties = new Dictionary<string, string>();
@@ -35,17 +54,10 @@ public CacheConfig(string region, string expiration, string sliding)
3554
Properties["cache.use_sliding_expiration"] = sliding;
3655
}
3756

38-
/// <summary>Whether this configuration is global or is a cache region configuration.</summary>
39-
public bool Global { get; }
40-
4157
/// <summary>The region name.</summary>
4258
public string Region { get; }
4359

44-
/// <summary>The <see cref="IDistributedCacheFactory"/> factory class name to use for getting
45-
/// <see cref="IDistributedCache"/> instances.</summary>
46-
public string FactoryClass { get; }
47-
48-
/// <summary>The configuration properties.</summary>
49-
public IDictionary<string,string> Properties { get; }
60+
/// <summary>The region configuration properties.</summary>
61+
public IDictionary<string, string> Properties { get; }
5062
}
5163
}

CoreDistributedCache/NHibernate.Caches.CoreDistributedCache/CoreDistributedCacheProvider.cs

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class CoreDistributedCacheProvider : ICacheProvider
3737
{
3838
private static readonly Dictionary<string, IDictionary<string, string>> ConfiguredCachesProperties;
3939
private static readonly INHibernateLogger Log;
40+
private static readonly System.Type[] CacheFactoryCtorWithPropertiesSignature = { typeof(IDictionary<string, string>) };
4041

4142
/// <summary>
4243
/// The <see cref="IDistributedCacheFactory"/> factory to use for getting <see cref="IDistributedCache"/>
@@ -53,31 +54,32 @@ static CoreDistributedCacheProvider()
5354
Log = NHibernateLogger.For(typeof(CoreDistributedCacheProvider));
5455
ConfiguredCachesProperties = new Dictionary<string, IDictionary<string, string>>();
5556

56-
if (!(ConfigurationManager.GetSection("coredistributedcache") is CacheConfig[] list))
57+
if (!(ConfigurationManager.GetSection("coredistributedcache") is CacheConfig config))
5758
return;
5859

59-
foreach (var cache in list)
60+
if (!string.IsNullOrEmpty(config.FactoryClass))
6061
{
61-
if (cache.Global)
62+
try
6263
{
63-
if (!string.IsNullOrEmpty(cache.FactoryClass))
64-
{
65-
try
66-
{
67-
CacheFactory =
68-
(IDistributedCacheFactory) Cfg.Environment.BytecodeProvider.ObjectsFactory
69-
.CreateInstance(ReflectHelper.ClassForName(cache.FactoryClass));
70-
}
71-
catch (Exception e)
72-
{
73-
throw new HibernateException(
74-
$"Could not create the {nameof(IDistributedCacheFactory)} factory from '{cache.FactoryClass}'.",
75-
e);
76-
}
77-
}
78-
continue;
64+
var factoryClass = ReflectHelper.ClassForName(config.FactoryClass);
65+
var ctorWithProperties = factoryClass.GetConstructor(CacheFactoryCtorWithPropertiesSignature);
66+
67+
CacheFactory = (IDistributedCacheFactory) (ctorWithProperties != null ?
68+
ctorWithProperties.Invoke(new object[] { config.Properties }):
69+
Cfg.Environment.BytecodeProvider.ObjectsFactory.CreateInstance(factoryClass));
7970
}
71+
catch (Exception e)
72+
{
73+
throw new HibernateException(
74+
$"Could not create the {nameof(IDistributedCacheFactory)} factory from '{config.FactoryClass}'. " +
75+
$"(It must implement {nameof(IDistributedCacheFactory)} and have a constructor accepting a " +
76+
$"{nameof(IDictionary<string, string>)} or have a parameterless constructor.)",
77+
e);
78+
}
79+
}
8080

81+
foreach (var cache in config.Regions)
82+
{
8183
ConfiguredCachesProperties.Add(cache.Region, cache.Properties);
8284
}
8385
}

CoreDistributedCache/NHibernate.Caches.CoreDistributedCache/CoreDistributedCacheSectionHandler.cs

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,49 +19,46 @@ public class CoreDistributedCacheSectionHandler : IConfigurationSectionHandler
1919
/// <param name="parent"></param>
2020
/// <param name="configContext"></param>
2121
/// <param name="section"></param>
22-
/// <returns>An array of CacheConfig objects.</returns>
22+
/// <returns>A <see cref="CacheConfig" /> object.</returns>
2323
public object Create(object parent, object configContext, XmlNode section)
2424
{
25-
var caches = new List<CacheConfig>();
25+
var caches = new List<RegionConfig>();
2626

27-
var fc = section.Attributes?["factory-class"];
28-
if (fc != null)
29-
{
30-
caches.Add(new CacheConfig(fc.Value));
31-
}
32-
3327
var nodes = section.SelectNodes("cache");
3428
foreach (XmlNode node in nodes)
3529
{
36-
string region = null;
37-
string expiration = null;
38-
string sliding = null;
39-
var r = node.Attributes["region"];
40-
var e = node.Attributes["expiration"];
41-
var s = node.Attributes["sliding"];
42-
if (r != null)
43-
{
44-
region = r.Value;
45-
}
46-
if (e != null)
30+
var region = node.Attributes["region"]?.Value;
31+
var expiration = node.Attributes["expiration"]?.Value;
32+
var sliding = node.Attributes["sliding"]?.Value;
33+
if (region != null)
4734
{
48-
expiration = e.Value;
35+
caches.Add(new RegionConfig(region, expiration, sliding));
4936
}
50-
if (s != null)
37+
else
5138
{
52-
sliding = s.Value;
39+
Log.Warn("Found a cache region node lacking a region name: ignored. Node: {0}",
40+
node.OuterXml);
5341
}
54-
if (region != null)
42+
}
43+
44+
var factoryClass = section.Attributes?["factory-class"]?.Value;
45+
var properties = new Dictionary<string, string>();
46+
nodes = section.SelectNodes("properties/property");
47+
foreach (XmlNode node in nodes)
48+
{
49+
var name = node.Attributes["name"]?.Value;
50+
if (name != null)
5551
{
56-
caches.Add(new CacheConfig(region, expiration, sliding));
52+
properties.Add(name, node.InnerText);
5753
}
5854
else
5955
{
60-
Log.Warn("Found a cache node lacking a region name: ignored. Node: {0}",
56+
Log.Warn("Found a cache property node lacking a name: ignored. Node: {0}",
6157
node.OuterXml);
6258
}
6359
}
64-
return caches.ToArray();
60+
61+
return new CacheConfig(factoryClass, properties, caches.ToArray());
6562
}
6663

6764
#endregion

0 commit comments

Comments
 (0)