best way to Lazy-Load a Dictionary' s 'Values ?
-
Here's one way:
// code here uses .NET 4.7
// in a NameSpace
public enum Divisions
{
Undefined,
Admin,
Finance,
Sales,
Research,
IT,
Temp
}// in a Class: assume 'Person is a typical POCO
public Dictionary>> DivisionToStaff { set; get; } =
new Dictionary>>();// initialization : typically done in a 'ctor
public Initialize()
{
foreach (Divisions div in Enum.GetValues(typeof(Divisions)))
{
DivisionToStaff.Add(div, new Lazy>());
}
}// sample use case:
// DivisionToStaff[personInstance.Division].Value.Add(personInstance);Do you know a better way ? thanks, Bill
«When I consider my brief span of life, swallowed up in an eternity before and after, the little space I fill, and even can see, engulfed in the infinite immensity of spaces of which I am ignorant, and which know me not, I am frightened, and am astonished at being here rather than there; for there is no reason why here rather than there, now rather than then.» Blaise Pascal
-
Here's one way:
// code here uses .NET 4.7
// in a NameSpace
public enum Divisions
{
Undefined,
Admin,
Finance,
Sales,
Research,
IT,
Temp
}// in a Class: assume 'Person is a typical POCO
public Dictionary>> DivisionToStaff { set; get; } =
new Dictionary>>();// initialization : typically done in a 'ctor
public Initialize()
{
foreach (Divisions div in Enum.GetValues(typeof(Divisions)))
{
DivisionToStaff.Add(div, new Lazy>());
}
}// sample use case:
// DivisionToStaff[personInstance.Division].Value.Add(personInstance);Do you know a better way ? thanks, Bill
«When I consider my brief span of life, swallowed up in an eternity before and after, the little space I fill, and even can see, engulfed in the infinite immensity of spaces of which I am ignorant, and which know me not, I am frightened, and am astonished at being here rather than there; for there is no reason why here rather than there, now rather than then.» Blaise Pascal
I have never used Lazy loading in my own applications, for various reasons. Anyways, looking at your code, Bill, I can say if I had to implement "lazy" loading like feature here, I would simply just set it to
null
. In most cases of Lazy loading — for instance, the Entity Framework navigation — the types are null, unless you actually specify to need them. Then, once it is required, I would load the data using theyield
operator; giving me somewhat relative of the lazy loading. But my answers depends on the way you fetch theList
, from database perhaps? I have not yet tried .NET 4.7 by now. :( Does it include anything new? Shiv provides a good overview, and to rewrite that in a different way, would require the understanding that he provides here, [Can you explain Lazy Loading?](https://www.codeproject.com/Articles/652556/Can-you-explain-Lazy-Loading) [Lazy Loading By Using C# Yield Statements | Freaking Awesome](http://blog.freakingawesome.net/2011/10/19/lazy-loading-by-using-c-yield-statements/)The shit I complain about It's like there ain't a cloud in the sky and it's raining out - Eminem ~! Firewall !~
-
Here's one way:
// code here uses .NET 4.7
// in a NameSpace
public enum Divisions
{
Undefined,
Admin,
Finance,
Sales,
Research,
IT,
Temp
}// in a Class: assume 'Person is a typical POCO
public Dictionary>> DivisionToStaff { set; get; } =
new Dictionary>>();// initialization : typically done in a 'ctor
public Initialize()
{
foreach (Divisions div in Enum.GetValues(typeof(Divisions)))
{
DivisionToStaff.Add(div, new Lazy>());
}
}// sample use case:
// DivisionToStaff[personInstance.Division].Value.Add(personInstance);Do you know a better way ? thanks, Bill
«When I consider my brief span of life, swallowed up in an eternity before and after, the little space I fill, and even can see, engulfed in the infinite immensity of spaces of which I am ignorant, and which know me not, I am frightened, and am astonished at being here rather than there; for there is no reason why here rather than there, now rather than then.» Blaise Pascal
Not sure if it's better, but something like this might work:
public class LazyDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
{
private readonly ConcurrentDictionary<TKey, TValue> _values;
private readonly Func<TKey, TValue> _valueFactory;
private readonly Func<TKey, bool> _keyValidator;public LazyDictionary(Func<TKey, TValue> valueFactory, Func<TKey, bool> keyValidator = null, IEqualityComparer<TKey> keyComparer = null) { if (valueFactory == null) throw new ArgumentNullException(nameof(valueFactory)); if (keyComparer == null) keyComparer = EqualityComparer<TKey>.Default; \_values = new ConcurrentDictionary<TKey, TValue>(keyComparer); \_valueFactory = valueFactory; \_keyValidator = keyValidator; } private bool KeyIsValid(TKey key) => \_keyValidator == null || \_keyValidator(key); private TValue GetOrAdd(TKey key) => \_values.GetOrAdd(key, \_valueFactory); public int Count => \_values.Count; public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => \_values.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public IEnumerable<TKey> Keys => \_values.Keys; public IEnumerable<TValue> Values => \_values.Values; public bool ContainsKey(TKey key) => KeyIsValid(key); public bool TryGetValue(TKey key, out TValue value) { if (!ContainsKey(key)) { value = default(TValue); return false; } value = GetOrAdd(key); return true; } public TValue this\[TKey key\] { get { if (!ContainsKey(key)) throw new KeyNotFoundException(); return GetOrAdd(key); } }
}
Usage:
public IReadOnlyDictionary<Division, IList<Person>> DivisionToStaff { get; }
= new LazyDictionary<Division, IList<Person>>(d => new List<Person>(), d => Enum.IsDefined(typeof(Division), d));...
DivisionToStaff[personInstance.Division].Add(personInstance);
ContainsKey
will returntrue
for any valid key.Count
,GetEnumerator
, and theKeys
andValues
properties will only reflect the keys which have already been created. -
I have never used Lazy loading in my own applications, for various reasons. Anyways, looking at your code, Bill, I can say if I had to implement "lazy" loading like feature here, I would simply just set it to
null
. In most cases of Lazy loading — for instance, the Entity Framework navigation — the types are null, unless you actually specify to need them. Then, once it is required, I would load the data using theyield
operator; giving me somewhat relative of the lazy loading. But my answers depends on the way you fetch theList
, from database perhaps? I have not yet tried .NET 4.7 by now. :( Does it include anything new? Shiv provides a good overview, and to rewrite that in a different way, would require the understanding that he provides here, [Can you explain Lazy Loading?](https://www.codeproject.com/Articles/652556/Can-you-explain-Lazy-Loading) [Lazy Loading By Using C# Yield Statements | Freaking Awesome](http://blog.freakingawesome.net/2011/10/19/lazy-loading-by-using-c-yield-statements/)The shit I complain about It's like there ain't a cloud in the sky and it's raining out - Eminem ~! Firewall !~
Quote:
if I had to implement "lazy" loading like feature here, I would simply just set it to null
Hi, Afzaal, I do not claim to be an expert on Lazy<T>: the question here comes out of my "quest" to better understand it. I would say that you use a Lazy<T> because instantiating one with 'new does not allocate memory for what it wraps. So you can eliminate guard-code like in the 'getter in this Property:
// C# 7: using expression-body member as 'setter or 'getter now allowed in properties that are not read-only
public List AllNodes
{
get => _allNodes ?? (_allNodes = new List());
private set => _allNodes = value;
}Even though I have not used the thread-safety features of Lazy<T>, it's clear those features are extensive. C# 7 ? : still trying to digest it :) Some well written articles by Jonathan Allen are helping: [^] cheers, Bill
«When I consider my brief span of life, swallowed up in an eternity before and after, the little space I fill, and even can see, engulfed in the infinite immensity of spaces of which I am ignorant, and which know me not, I am frightened, and am astonished at being here rather than there; for there is no reason why here rather than there, now rather than then.» Blaise Pascal
-
Not sure if it's better, but something like this might work:
public class LazyDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
{
private readonly ConcurrentDictionary<TKey, TValue> _values;
private readonly Func<TKey, TValue> _valueFactory;
private readonly Func<TKey, bool> _keyValidator;public LazyDictionary(Func<TKey, TValue> valueFactory, Func<TKey, bool> keyValidator = null, IEqualityComparer<TKey> keyComparer = null) { if (valueFactory == null) throw new ArgumentNullException(nameof(valueFactory)); if (keyComparer == null) keyComparer = EqualityComparer<TKey>.Default; \_values = new ConcurrentDictionary<TKey, TValue>(keyComparer); \_valueFactory = valueFactory; \_keyValidator = keyValidator; } private bool KeyIsValid(TKey key) => \_keyValidator == null || \_keyValidator(key); private TValue GetOrAdd(TKey key) => \_values.GetOrAdd(key, \_valueFactory); public int Count => \_values.Count; public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => \_values.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public IEnumerable<TKey> Keys => \_values.Keys; public IEnumerable<TValue> Values => \_values.Values; public bool ContainsKey(TKey key) => KeyIsValid(key); public bool TryGetValue(TKey key, out TValue value) { if (!ContainsKey(key)) { value = default(TValue); return false; } value = GetOrAdd(key); return true; } public TValue this\[TKey key\] { get { if (!ContainsKey(key)) throw new KeyNotFoundException(); return GetOrAdd(key); } }
}
Usage:
public IReadOnlyDictionary<Division, IList<Person>> DivisionToStaff { get; }
= new LazyDictionary<Division, IList<Person>>(d => new List<Person>(), d => Enum.IsDefined(typeof(Division), d));...
DivisionToStaff[personInstance.Division].Add(personInstance);
ContainsKey
will returntrue
for any valid key.Count
,GetEnumerator
, and theKeys
andValues
properties will only reflect the keys which have already been created.Wow ! That's the Ph.D. answer. My reading of the Lazy<T> docs suggests its multi-threading features are robust. thanks, Bill
«When I consider my brief span of life, swallowed up in an eternity before and after, the little space I fill, and even can see, engulfed in the infinite immensity of spaces of which I am ignorant, and which know me not, I am frightened, and am astonished at being here rather than there; for there is no reason why here rather than there, now rather than then.» Blaise Pascal
-
Quote:
if I had to implement "lazy" loading like feature here, I would simply just set it to null
Hi, Afzaal, I do not claim to be an expert on Lazy<T>: the question here comes out of my "quest" to better understand it. I would say that you use a Lazy<T> because instantiating one with 'new does not allocate memory for what it wraps. So you can eliminate guard-code like in the 'getter in this Property:
// C# 7: using expression-body member as 'setter or 'getter now allowed in properties that are not read-only
public List AllNodes
{
get => _allNodes ?? (_allNodes = new List());
private set => _allNodes = value;
}Even though I have not used the thread-safety features of Lazy<T>, it's clear those features are extensive. C# 7 ? : still trying to digest it :) Some well written articles by Jonathan Allen are helping: [^] cheers, Bill
«When I consider my brief span of life, swallowed up in an eternity before and after, the little space I fill, and even can see, engulfed in the infinite immensity of spaces of which I am ignorant, and which know me not, I am frightened, and am astonished at being here rather than there; for there is no reason why here rather than there, now rather than then.» Blaise Pascal
I have also not worked too much in this context because most of my data sources are SQL based databases, so I try to load only the data that I require,
SELECT TOP 10 FROM table_name ...
That works like a charm in my cases.
BillWoodruff wrote:
instantiating one with 'new does not allocate memory for what it wraps
Of course, that is why I provided the Entity Framework example, they provide a function
Include
, which you execute and they then load the rest of the data, until then the objects are null. That EF code, gave me a hint that in most cases I should just leave the objects to null and then yield them when needed; this works in some cases. Thisyield
does somewhat better, as it does provide the objects when we actually iterate over them (use them). As for C# 7, I just didn't get enough time to actually experiment around with them like [I did with C# 6](https://www.codeproject.com/Articles/1073905/Experimenting-with-Csharp-s-new-features), as I was busy with my final year project and some academics stuff. :-)The shit I complain about It's like there ain't a cloud in the sky and it's raining out - Eminem ~! Firewall !~
-
Wow ! That's the Ph.D. answer. My reading of the Lazy<T> docs suggests its multi-threading features are robust. thanks, Bill
«When I consider my brief span of life, swallowed up in an eternity before and after, the little space I fill, and even can see, engulfed in the infinite immensity of spaces of which I am ignorant, and which know me not, I am frightened, and am astonished at being here rather than there; for there is no reason why here rather than there, now rather than then.» Blaise Pascal
The
Lazy<T>
threading support can be robust, depending on whichLazyThreadSafetyMode
value you pass to the constructor. (The default isExecutionAndPublication
, which is the most robust.) But theDictionary<TKey, TValue>
definitely isn't thread-safe if you modify it. It's not a problem if you're pre-populating the dictionary with aLazy<T>
for every possible key, and never modifying the contents of the dictionary. But if you have a large set of keys, some of which are rarely used, you might want to avoid the memory overhead.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer