Thread-safe repositories
Posted by Davy Brion on 25th March 2008
First of all, this is not about true DDD repositories… i just needed an in-memory data container with basic storing/retrieving functionality so i figured i’d call it a Repository.
These repositories will be accessed by multiple concurrent threads so i needed to make sure that all access to the underlying dictionary is properly synchronized. But, i also want them to be highly usable and i especially wanted a thread-safe way of dynamically executing queries on them.
Here’s what i came up with:
public class Repository<T> where T : Member
{
private readonly object _monitor = new object();
private readonly Dictionary<Id, T> _members = new Dictionary<Id,T>();
protected object Monitor
{
get { return _monitor; }
}
/// <summary>
/// any derived classes that use this property are responsible for their own locking!
/// (use the Monitor property for that)
/// </summary>
protected Dictionary<Id, T> Members
{
get { return _members; }
}
public virtual void Store(T member)
{
lock (Monitor)
{
Put(member);
}
}
public virtual void StoreRange(IEnumerable<T> members)
{
lock (Monitor)
{
foreach (T member in members)
{
Put(member);
}
}
}
public virtual void Remove(T member)
{
lock (Monitor)
{
if (Members.ContainsValue(member))
{
Members.Remove(member.Id);
}
}
}
public virtual T Get(Id id)
{
lock (Monitor)
{
if (!Members.ContainsKey(id))
{
return null;
}
return Members[id];
}
}
public virtual T FindFirst(Func<T, bool> expression)
{
lock (Monitor)
{
return Members.Values.FirstOrDefault(expression);
}
}
public virtual IEnumerable<T> FindAll(Func<T, bool> expression)
{
lock (Monitor)
{
return Members.Values.Where(expression).ExecuteImmediately();
}
}
/// <summary>
/// this method should ONLY be called when a lock on Monitor has been acquired!
/// </summary>
/// <param name=”member”></param>
private void Put(T member)
{
// this overwrites existing entries with the same ID… it’s not a mistake
Members[member.Id] = member;
}
}
each access to the dictionary is properly synchronized (at least, i think so… if anyone knows of a better way to do the locking, please leave a comment) and i can still execute dynamic queries on them by passing lambda expressions to the FindFirst and FindAll methods, like this:
User me = userReposority.FindFirst(u => u.FirstName == “Davy” && u.LastName == “Brion”);
IEnumerable<User> usersWithNoLastName =
userReposority.FindAll(u => string.IsNullOrEmpty(u.LastName));
Posted in Multithreading, Software Development | 2 Comments »


