Skip to content

Commit 42f4c61

Browse files
author
Claus
committed
Merge branch 'feature/35-boost-publish-of-archetypes' of https://github.com/Nicholas-Westby/Umbraco.Deploy.Contrib into Nicholas-Westby-feature/35-boost-publish-of-archetypes
2 parents dd10189 + 5cd531c commit 42f4c61

File tree

4 files changed

+437
-44
lines changed

4 files changed

+437
-44
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
namespace Umbraco.Deploy.Contrib.Connectors.Caching.Comparers
5+
{
6+
7+
/// <summary>
8+
/// Compares an array of strings.
9+
/// </summary>
10+
/// <remarks>
11+
/// Based on: https://github.com/rhythmagency/rhythm.caching.core/blob/master/src/Rhythm.Caching.Core/Comparers/StringArrayComparer.cs
12+
/// </remarks>
13+
public class StringArrayComparer : IEqualityComparer<string[]>
14+
{
15+
16+
/// <summary>
17+
/// Check if the arrays are equal.
18+
/// </summary>
19+
/// <param name="x">
20+
/// The first array.
21+
/// </param>
22+
/// <param name="y">
23+
/// The second array.
24+
/// </param>
25+
/// <returns>
26+
/// True, if the arrays are both null, are both empty, or both have
27+
/// the same strings in the same order; otherwise, false.
28+
/// </returns>
29+
public bool Equals(string[] x, string[] y)
30+
{
31+
if (x == null || y == null)
32+
{
33+
return x == null && y == null;
34+
}
35+
if (x.Length != y.Length)
36+
{
37+
return false;
38+
}
39+
for (var i = 0; i < x.Length; i++)
40+
{
41+
if (x[i] != y[i])
42+
{
43+
return false;
44+
}
45+
}
46+
return true;
47+
}
48+
49+
/// <summary>
50+
/// Generates a hash code by combining all of the hash codes for the strings in the array.
51+
/// </summary>
52+
/// <param name="items">
53+
/// The array of strings.
54+
/// </param>
55+
/// <returns>
56+
/// The combined hash code.
57+
/// </returns>
58+
public int GetHashCode(string[] items)
59+
{
60+
if (items == null || !items.Any())
61+
{
62+
return 0;
63+
}
64+
else
65+
{
66+
var hashCode = default(int);
67+
foreach (var item in items)
68+
{
69+
hashCode ^= (item ?? string.Empty).GetHashCode();
70+
}
71+
return hashCode;
72+
}
73+
}
74+
75+
}
76+
77+
}
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Umbraco.Deploy.Contrib.Connectors.Caching.Comparers;
4+
5+
namespace Umbraco.Deploy.Contrib.Connectors.Caching
6+
{
7+
8+
/// <summary>
9+
/// Caches instance variables by key in a dictionary-like structure.
10+
/// </summary>
11+
/// <typeparam name="T">
12+
/// The type of value to cache.
13+
/// </typeparam>
14+
/// <typeparam name="TKey">
15+
/// The key to use when access values in the dictionary.
16+
/// </typeparam>
17+
/// <remarks>
18+
/// Adapted from: https://github.com/rhythmagency/rhythm.caching.core/blob/master/src/Rhythm.Caching.Core/Caches/InstanceByKeyCache.cs
19+
/// </remarks>
20+
public class InstanceByKeyCache<T, TKey>
21+
{
22+
23+
/// <summary>
24+
/// An empty array (convenience variable).
25+
/// </summary>
26+
private static string[] EmptyArray = new string[] { };
27+
28+
/// <summary>
29+
/// The instances stored by their key, then again by a contextual key.
30+
/// </summary>
31+
private Dictionary<TKey, Tuple<Dictionary<string[], T>, DateTime>> Instances { get; set; }
32+
33+
/// <summary>
34+
/// Object to perform locks for cross-thread safety.
35+
/// </summary>
36+
private object InstancesLock { get; set; }
37+
38+
/// <summary>
39+
/// Default constructor.
40+
/// </summary>
41+
public InstanceByKeyCache()
42+
{
43+
InstancesLock = new object();
44+
Instances = new Dictionary<TKey, Tuple<Dictionary<string[], T>, DateTime>>();
45+
}
46+
47+
/// <summary>
48+
/// Gets the instance variable (either from the cache or from the specified function).
49+
/// </summary>
50+
/// <param name="key">
51+
/// The key to use when fetching the variable.
52+
/// </param>
53+
/// <param name="replenisher">
54+
/// The function that replenishes the cache.
55+
/// </param>
56+
/// <param name="duration">
57+
/// The duration to cache for.
58+
/// </param>
59+
/// <param name="method">
60+
/// Optional. The cache method to use when retrieving the value.
61+
/// </param>
62+
/// <param name="keys">
63+
/// Optional. The keys to store/retrieve a value by. Each key combination will
64+
/// be treated as a separate cache.
65+
/// </param>
66+
/// <returns>
67+
/// The value.
68+
/// </returns>
69+
public T Get(TKey key, Func<TKey, T> replenisher, TimeSpan duration, params string[] keys)
70+
{
71+
lock (InstancesLock)
72+
{
73+
74+
// Variables.
75+
var tempInstance = default(T);
76+
var now = DateTime.Now;
77+
78+
// Value already cached?
79+
var tempTuple = default(Tuple<Dictionary<string[], T>, DateTime>);
80+
if (Instances.TryGetValue(key, out tempTuple)
81+
&& tempTuple.Item1.ContainsKey(keys))
82+
{
83+
if (now.Subtract(Instances[key].Item2) >= duration)
84+
{
85+
86+
// Cache expired. Replenish the cache.
87+
tempInstance = replenisher(key);
88+
UpdateValueByKeys(keys, key, tempInstance, now, false);
89+
90+
}
91+
else
92+
{
93+
94+
// Cache still valid. Use cached value.
95+
tempInstance = TryGetByKeys(keys, key);
96+
97+
}
98+
}
99+
else
100+
{
101+
102+
// No cached value. Replenish the cache.
103+
tempInstance = replenisher(key);
104+
UpdateValueByKeys(keys, key, tempInstance, now, false);
105+
106+
}
107+
108+
// Return the instance.
109+
return tempInstance;
110+
111+
}
112+
}
113+
114+
/// <summary>
115+
/// Clears the cache.
116+
/// </summary>
117+
public void Clear()
118+
{
119+
lock (InstancesLock)
120+
{
121+
Instances.Clear();
122+
}
123+
}
124+
125+
/// <summary>
126+
/// Clears the cache of the specified keys.
127+
/// </summary>
128+
/// <param name="keys">
129+
/// The keys to clear the cache of.
130+
/// </param>
131+
public void ClearKeys(IEnumerable<TKey> keys)
132+
{
133+
lock (InstancesLock)
134+
{
135+
foreach (var key in keys)
136+
{
137+
Instances.Remove(key);
138+
}
139+
}
140+
}
141+
142+
/// <summary>
143+
/// Trys to get the value by the specified keys.
144+
/// </summary>
145+
/// <param name="keys">
146+
/// The keys.
147+
/// </param>
148+
/// <param name="accessKey">
149+
/// The key to use to access the value.
150+
/// </param>
151+
/// <returns>
152+
/// The value, or the default for the type.
153+
/// </returns>
154+
private T TryGetByKeys(string[] keys, TKey accessKey)
155+
{
156+
var chosenKeys = keys ?? EmptyArray;
157+
var value = default(T);
158+
lock (InstancesLock)
159+
{
160+
var valueDictionary = default(Tuple<Dictionary<string[], T>, DateTime>);
161+
if (Instances.TryGetValue(accessKey, out valueDictionary))
162+
{
163+
if (valueDictionary.Item1.TryGetValue(chosenKeys, out value))
164+
{
165+
return value;
166+
}
167+
}
168+
}
169+
return default(T);
170+
}
171+
172+
/// <summary>
173+
/// Updates the cache value by the specified keys.
174+
/// </summary>
175+
/// <param name="keys">
176+
/// The keys to cache by.
177+
/// </param>
178+
/// <param name="accessKey">
179+
/// The key to use to access the value.
180+
/// </param>
181+
/// <param name="value">
182+
/// The value to update the cache with.
183+
/// </param>
184+
/// <param name="lastCache">
185+
/// The date/time to mark the cache as last updated.
186+
/// </param>
187+
/// <param name="doLock">
188+
/// Lock the instance cache during the update?
189+
/// </param>
190+
private void UpdateValueByKeys(string[] keys, TKey accessKey, T value,
191+
DateTime lastCache, bool doLock = true)
192+
{
193+
if (doLock)
194+
{
195+
lock (InstancesLock)
196+
{
197+
UpdateValueByKeysWithoutLock(keys, accessKey, value, lastCache);
198+
}
199+
}
200+
else
201+
{
202+
UpdateValueByKeysWithoutLock(keys, accessKey, value, lastCache);
203+
}
204+
}
205+
206+
/// <summary>
207+
/// Updates the cache with the specified value.
208+
/// </summary>
209+
/// <param name="keys">
210+
/// The keys to cache by.
211+
/// </param>
212+
/// <param name="accessKey">
213+
/// The key to use to access the value.
214+
/// </param>
215+
/// <param name="value">
216+
/// The value to update the cache with.
217+
/// </param>
218+
private void UpdateValueByKeysWithoutLock(string[] keys, TKey accessKey,
219+
T value, DateTime lastCache)
220+
{
221+
222+
// Variables.
223+
var instanceTuple = default(Tuple<Dictionary<string[], T>, DateTime>);
224+
var instanceDictionary = default(Dictionary<string[], T>);
225+
226+
// Get or create the dictionary.
227+
if (Instances.TryGetValue(accessKey, out instanceTuple))
228+
{
229+
instanceDictionary = instanceTuple.Item1;
230+
}
231+
else
232+
{
233+
instanceDictionary = new Dictionary<string[], T>(new StringArrayComparer());
234+
}
235+
236+
// Update the value in the dictionary.
237+
instanceDictionary[keys] = value;
238+
239+
// Update the last cache date.
240+
Instances[accessKey] = new Tuple<Dictionary<string[], T>, DateTime>(
241+
instanceDictionary, lastCache);
242+
243+
}
244+
245+
}
246+
247+
}

src/Umbraco.Deploy.Contrib.Connectors/Umbraco.Deploy.Contrib.Connectors.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
</Reference>
6767
</ItemGroup>
6868
<ItemGroup>
69+
<Compile Include="Caching\Comparers\StringArrayComparer.cs" />
70+
<Compile Include="Caching\InstanceByKeyCache.cs" />
6971
<Compile Include="GridCellValueConnectors\DocTypeGridEditorCellValueConnector.cs" />
7072
<Compile Include="GridCellValueConnectors\LeBlenderGridCellValueConnector.cs" />
7173
<Compile Include="Properties\AssemblyInfo.cs" />

0 commit comments

Comments
 (0)