by dotnetnerd
18. November 2017 20:25
I recently worked on a long running migration project, where we needed to ensure that some of the sql migration scripts were only called once per entity that was migrated, in a threadsafe manner. I looked to the System.Collections.Concurrent namespace, but none of the classes were really a good match. Basically what I needed was the ability to run a part of the migration that was identified by key, but ensure that it would only run once, although different parent strategies might try and run it.
The closest thing I could find was the ConcurrentDictionary. It does however not gurantee that the function passed to GetOrAdd is not called multiple times, and of course I did not really need for it to be a dictionary. I came up with this little implementation, that uses the ConcurrentDictionary internally and combines it with Lazy<T> to ensure that the code only runs once.
using System;
using System.Collections.Concurrent;
using System.Threading;
namespace MyAwesomeSauce
{
public class OnetimeCollection<TKey>
{
private readonly ConcurrentDictionary<TKey, Lazy<string>> concurrentDictionary
= new ConcurrentDictionary<TKey, Lazy<string>>();
public void Run(TKey key, Action<TKey> valueFactory)
{
var lazyResult = concurrentDictionary.GetOrAdd(key, k =>
new Lazy<string>(() => {
valueFactory(k);
return string.Empty;
}, LazyThreadSafetyMode.ExecutionAndPublication));
var holeInTheGround = lazyResult.Value;
}
}
}
With this in place all I needed was a singleton instance, and to wrap each job in a call to Run. Nice and pragmatic solution.