Skip to content

Commit

Permalink
DynamicObject does not go into the cache
Browse files Browse the repository at this point in the history
Whenever a DynamicObject derived class was created, and another
DynamicObject was created from the same derived class, Mustache# was
using the old recipe for the object, because the properties were
actually being cached. While this is useful for a statically-typed
object, in the case of the DynamicObject, this is not practical.
Introduced a new flag in PropertyDictionary, _isDynamicObject, and check
the flag before performing operations against the cache. Static objects
still use the cache, dynamic objects get queried at run-time. This is
the safest bet to make sure all DynamicObject properties will be
available to the Render() call.
  • Loading branch information
daball committed Nov 29, 2014
1 parent 05a7da4 commit 90ef52c
Showing 1 changed file with 113 additions and 39 deletions.
152 changes: 113 additions & 39 deletions mustache-sharp/PropertyDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ internal sealed class PropertyDictionary : IDictionary<string, object>
private static readonly Dictionary<Type, Dictionary<string, Func<object, object>>> _cache = new Dictionary<Type, Dictionary<string, Func<object, object>>>();

private readonly object _instance;
private readonly bool _isDynamicObject;
private readonly Dictionary<string, Func<object, object>> _typeCache;

/// <summary>
Expand All @@ -31,6 +32,7 @@ public PropertyDictionary(object instance)
}
else
{
_isDynamicObject = isDynamicObject(_instance);
lock (_cache)
{
_typeCache = getCacheType(_instance);
Expand Down Expand Up @@ -60,29 +62,25 @@ public PropertyDictionary(object instance)
typeCache.Add(fieldInfo.Name, i => fieldInfo.GetValue(i));
}

var dynamicMembers = getDynamicMembers(instance);
foreach (KeyValuePair<string, object> member in dynamicMembers)
{
typeCache.Add(member.Key, i => member.Value);
}
//Disable cache of dynamic members, does not refresh for mutated objects of the same base type derived from DynamicObject:
//var dynamicMembers = getDynamicMembers(instance);
//foreach (KeyValuePair<string, object> member in dynamicMembers)
//{
// typeCache[member.Key] = (i => member.Value);
//}

_cache.Add(type, typeCache);
}
return typeCache;
}

private static IEnumerable<KeyValuePair<string, object>> getDynamicMembers(object instance)
private static bool isDynamicObject(object instance)
{
Dictionary<string, object> members = new Dictionary<string, object>();

Type type = instance.GetType();
Type baseType = type.BaseType;
bool isDynamicObject = false;

if (baseType == null)
return members;

while (baseType != baseType.BaseType && baseType.BaseType != null)
while (baseType.BaseType != null)
{
isDynamicObject = (baseType.FullName == "System.Dynamic.DynamicObject");
if (isDynamicObject)
Expand All @@ -91,19 +89,26 @@ public PropertyDictionary(object instance)
baseType = baseType.BaseType;
}

if (isDynamicObject)
{
DynamicObject dynamicInstance = (DynamicObject)instance;
dynamic dynamicObject = dynamicInstance;
return isDynamicObject;
}

foreach (string member in dynamicInstance.GetDynamicMemberNames())
{
members.Add(member, Dynamitey.Dynamic.InvokeGet(instance, member));
}
private static IEnumerable<KeyValuePair<string, Func<object, object>>> getDynamicMembers(object instance)
{
Dictionary<string, Func<object, object>> members = new Dictionary<string, Func<object, object>>();
DynamicObject dynamicInstance = (DynamicObject)instance;
foreach (string member in dynamicInstance.GetDynamicMemberNames())
{
members.Add(member, getDynamicMemberValueGetter(instance, member));
}
return members;
}

private static Func<object, object> getDynamicMemberValueGetter(object instance, string memberName)
{
DynamicObject dynamicInstance = (DynamicObject)instance;
return i => Dynamitey.Dynamic.InvokeGet(i, memberName);
}

private static IEnumerable<TMember> getMembers<TMember>(Type type, IEnumerable<TMember> members)
where TMember : MemberInfo
{
Expand Down Expand Up @@ -156,15 +161,24 @@ public object Instance
/// <returns>True if the property exists; otherwise, false.</returns>
public bool ContainsKey(string key)
{
return _typeCache.ContainsKey(key);
if (_isDynamicObject)
return ((DynamicObject)_instance).GetDynamicMemberNames().Contains(key);
else
return _typeCache.ContainsKey(key);
}

/// <summary>
/// Gets the name of the properties in the type.
/// </summary>
public ICollection<string> Keys
{
get { return _typeCache.Keys; }
get
{
if (_isDynamicObject)
return ((DynamicObject)_instance).GetDynamicMemberNames().ToArray();
else
return _typeCache.Keys;
}
}

[EditorBrowsable(EditorBrowsableState.Never)]
Expand All @@ -183,10 +197,22 @@ public ICollection<string> Keys
public bool TryGetValue(string key, out object value)
{
Func<object, object> getter;
if (!_typeCache.TryGetValue(key, out getter))
if (_isDynamicObject)
{
if (!((DynamicObject)_instance).GetDynamicMemberNames().Contains(key))
{
value = null;
return false;
}
getter = getDynamicMemberValueGetter(_instance, key);
}
else
{
value = null;
return false;
if (!_typeCache.TryGetValue(key, out getter))
{
value = null;
return false;
}
}
value = getter(_instance);
return true;
Expand All @@ -199,12 +225,23 @@ public ICollection<object> Values
{
get
{
ICollection<Func<object, object>> getters = _typeCache.Values;
List<object> values = new List<object>();
foreach (Func<object, object> getter in getters)
if (_isDynamicObject)
{
object value = getter(_instance);
values.Add(value);
foreach (KeyValuePair<string, Func<object, object>> member in getDynamicMembers(_instance))
{
object value = member.Value(_instance);
values.Add(value);
}
}
else
{
ICollection<Func<object, object>> getters = _typeCache.Values;
foreach (Func<object, object> getter in getters)
{
object value = getter(_instance);
values.Add(value);
}
}
return values.AsReadOnly();
}
Expand All @@ -225,8 +262,16 @@ public ICollection<object> Values
{
get
{
Func<object, object> getter = _typeCache[key];
return getter(_instance);
if (_isDynamicObject)
{
Func<object, object> getter = getDynamicMemberValueGetter(_instance, key);
return getter(_instance);
}
else
{
Func<object, object> getter = _typeCache[key];
return getter(_instance);
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
set
Expand All @@ -250,9 +295,20 @@ public ICollection<object> Values
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
{
Func<object, object> getter;
if (!_typeCache.TryGetValue(item.Key, out getter))
if (_isDynamicObject)
{
if (!((DynamicObject)_instance).GetDynamicMemberNames().Contains(item.Key))
{
return false;
}
getter = getDynamicMemberValueGetter(_instance, item.Key);
}
else
{
return false;
if (!_typeCache.TryGetValue(item.Key, out getter))
{
return false;
}
}
object value = getter(_instance);
return Equals(item.Value, value);
Expand All @@ -276,7 +332,13 @@ public ICollection<object> Values
/// </summary>
public int Count
{
get { return _typeCache.Count; }
get
{
if (_isDynamicObject)
return ((DynamicObject)_instance).GetDynamicMemberNames().Count();
else
return _typeCache.Count;
}
}

/// <summary>
Expand All @@ -294,16 +356,28 @@ public int Count
}

/// <summary>
/// Gets the propety name/value pairs in the object.
/// Gets the property name/value pairs in the object.
/// </summary>
/// <returns></returns>
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
foreach (KeyValuePair<string, Func<object, object>> pair in _typeCache)
if (_isDynamicObject)
{
Func<object, object> getter = pair.Value;
object value = getter(_instance);
yield return new KeyValuePair<string, object>(pair.Key, value);
foreach (KeyValuePair<string, Func<object, object>> pair in getDynamicMembers(_instance))
{
Func<object, object> getter = pair.Value;
object value = getter(_instance);
yield return new KeyValuePair<string, object>(pair.Key, value);
}
}
else
{
foreach (KeyValuePair<string, Func<object, object>> pair in _typeCache)
{
Func<object, object> getter = pair.Value;
object value = getter(_instance);
yield return new KeyValuePair<string, object>(pair.Key, value);
}
}
}

Expand Down

0 comments on commit 90ef52c

Please sign in to comment.