Say I have several List properties. Something Like this:
List<CustomerTypes> CustomerTypes {get; set;}
List<FormatTypes> FormatTypes {get; set;}
List<WidgetTypes> WidgetTypes {get; set}
List<PriceList> PriceList {get; set;}
Because these values update very rarely, I am caching them in my WCF Service at startup. I then have a service operation that can be called to refresh them.
The service operation will query them all from the database something like this:
// Get the data from the database.
var customerTypes = dbContext.GetCustomerTypes();
var formatTypes = dbContext.GetFormatTypes();
var widgetTypes = dbContext.GetWidgetTypes ();
var priceList = dbContext.GetPriceList ();
// Update the references
CustomerTypes = customerTypes;
FormatTypes = formatTypes;
WidgetTypes = widgetTypes;
PriceList = priceList;
This results in very little time that these are not all in sync. However, they are not fully thread safe. (A call could access a new CustomerType and an old PriceList.)
How can I make it so that while I am updating the references, any use of these lists has to wait until all references have been updated?
First put all of those lists in to a single container class.
Class TypeLists
{
List<CustomerTypes> CustomerTypes {get; set;}
List<FormatTypes> FormatTypes {get; set;}
List<WidgetTypes> WidgetTypes {get; set}
List<PriceList> PriceList {get; set;}
}
Then replace the old property accesses with a function call.
private readonly object _typeListsLookupLock = new object();
private volatile TypeLists _typeLists;
private volatile DateTime _typeListAge;
public TypeLists GetTypeList()
{
if(_typeLists == null || DateTime.UtcNow - _typeListAge > MaxCacheAge)
{
//The assignment of _typeLists is thread safe, this lock is only to
//prevent multiple concurrent database lookups. If you don't care that
//two threads could call GetNewTypeList() at the same time you can remove
//the lock and inner if check.
lock(_typeListsLookupLock)
{
//Check to see if while we where waiting to enter the lock someone else
//updated the lists and making the call to the database unnecessary.
if(_typeLists == null || DateTime.UtcNow - _typeListAge > MaxCacheAge)
{
_typeLists = GetNewTypeList();
_typeListAge = DateTime.UtcNow;
}
}
}
return _typeLists;
}
private TypeLists GetNewTypeList()
{
var container = new TypeLists()
using(var dbContext = GetContext())
{
container.CustomerTypes = dbContext.GetCustomerTypes();
container.FormatTypes = dbContext.GetFormatTypes();
container.WidgetTypes = dbContext.GetFormatTypes();
container.PriceList = dbContext.GetPriceList ();
}
return container;
}
The reason we change from a property to a function is you did
SomeFunction(myClass.TypeLists.PriceList, myClass.TypeLists.FormatTypes);
You could have TypeLists
changed out from under you in a multi-threaded environment, however if you do
var typeLists = myClass.GetTypeLists();
SomeFunction(typeLists.PriceList, typeLists.FormatTypes);
that typeLists
object is not mutated between threads so you do not need to worry about it's value changing out from under you, you could do var typeLists = myClass.TypeLists
but making it a function makes it is more clear that you could potentially get different results between calls.
If you want to be fancy you can change GetTypeList()
so it uses a MemoryCache
to detect when it should expire the object and get a new one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With