More fun with dictionaries on the way. We’ve seen how to read from a dictionary with the GetValueOrDefault() extension method but that’s not enough.
We usually need a way to add items to a dictionary if they aren’t already there. Here’s a simple example of a caching mechanism that relies on in-memory dictionaries:
Given:
public interface IOrderRepository
{
Order GetOrderById(int id);
}
public class Order
{
public int Id { get; set; }
}
A simple caching mechanism can be obtained using the Decorator pattern:
public class CacheOrderRepository : IOrderRepository
{
private Dictionary<int, Order> orders = new Dictionary<int, Order>();
public CacheOrderRepository(IOrderRepository repository)
{
Repository = repository;
}
public IOrderRepository Repository
{
get;
private set;
}
#region IOrderRepository Members
public Order GetOrderById(int id)
{
Order order;
if (!orders.TryGetValue(id, out order))
{
order = Repository.GetOrderById(id);
orders.Add(id, order);
}
return order;
}
#endregion
}
What basically happens here is that we’ll first try to find the value from the local dictionary (hence the cache) and if not present, go to the real repository to fetch the value and then create an entry in the local cache with the result. So what the code really wants to say is:
public Order GetOrderById(int id)
{
return orders.FindOrCreate(id, () => Repository.GetOrderById(id));
}
A simple refactoring of the previous implementation into an extension method would lead to something like:
public static class DictionaryExtensions
{
public static TValue FindOrCreate<TKey, TValue>(
this IDictionary<TKey, TValue> items, TKey key,
Func<TValue> factory)
{
TValue value;
if (!items.TryGetValue(key, out value))
{
value = factory();
items.Add(key, value);
}
return value;
}
}
Note again that we’re extending the minimal interface, IDictionary<T>, and leveraging an existing delegate type, Func<T>.
() => “Francois”