I have 2 dictionaries in a scoped service within a Blazor server application I use to manage state for multi tenancy. It has come to my attention that there may be concurrency issues with users modifying the dictionaries on different threads.
public Dictionary<string, GlobalDataModel> Global { get; } = new();
public Dictionary<string, Dictionary<long, LocalDataModel>> Local { get; } = new();
I think I'm leaning towards leaving these as normal dictionaries and locking sync root when modifying or iterating over.
If I were to add an item to a collection within the containing class, would it be:
if (Local.ContainsKey(tenantId) == false)
{
lock (syncRoot)
{
Local.Add(tenantId, new Dictionary<long, LocalDataModel>());
}
}
or:
lock (syncRoot)
{
if (Local.ContainsKey(tenantId) == false)
{
{
Local.Add(tenantId, new Dictionary<long, LocalDataModel>());
}
}
}
Then if I were to copy different parts of the service collection to lists from an external class so it can be safely iterated would I just lock at the Service level, the Dictionary level, or the DataModel level, or is it dependent?
Say I wanted a resources collection within a specific project. Would it be:
[Inject] private IDataAdaptor DataAdaptor { get; set; };
var resources;
lock (DataAdaptor)
{
resources = DataAdaptor.Local[TenantId][ProjectId].Resources;
}
or:
lock (DataAdaptor.Local[TenantId][ProjectId].Resources)
{
resources = DataAdaptor.Local[TenantId][ProjectId].Resources;
}
And vice versa for different parts of the collection, et Tenants, Projects etc...
I assume I have to lock on the object because syncRoot
isn't accessible outside the containing class, or do I create a SyncRoot
object in the class where I'm copying and lock on that?
Obviously multi threading is a new concept.