Skip to content

Instantly share code, notes, and snippets.

@ericdes
Created February 27, 2016 18:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ericdes/caa82ce357c98ce8f4cb to your computer and use it in GitHub Desktop.
Save ericdes/caa82ce357c98ce8f4cb to your computer and use it in GitHub Desktop.
TelaReactiveSourceCache
private void LoadFromJson_Click(object sender, RoutedEventArgs e)
{
if (_json == null) return;
var myTrades = JsonConvert.DeserializeObject<TelaReactiveSourceCache<Trade, string>>(_json);
ViewModel.TradesSource = myTrades;
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ReactiveUI;
using System.Collections;
using System.Collections.Specialized;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Diagnostics;
using DynamicData;
namespace Tela.Firebase.Reactive
{
/// <summary>
/// An implementation of dynamic data source that serializes as a JSON dictionary.
/// </summary>
/// <typeparam name="V">Value type</typeparam>
/// <typeparam name="K">Key type</typeparam>
[JsonConverter(typeof(TelaReactiveSourceCacheSerializer))]
public class TelaReactiveSourceCache<V, K> : SourceCache<V, K>
where K : IEquatable<string>
{
/// <summary>
/// Property information about the matching key in type V.
/// Notice: static in generic class -> it's per each combination of V, K.
/// </summary>
private static PropertyInfo _vKeyInfo;
/// <summary>
/// Selector function for the key in type V.
/// </summary>
private static Func<V, K> _vKeySelector;
public Func<V, K> KeySelector
{
get { return _vKeySelector; }
}
/// <summary>
/// Get the K matching key in V with the convention that first argument in constructor of V is they key.
/// </summary>
/// <returns></returns>
private static PropertyInfo GetPropertyInfoOfValueKey()
{
var ctors = typeof(V).GetConstructors();
Debug.Assert(ctors != null && ctors.Length > 0, string.Format("Expecting a constructor for type {0}.", typeof(V)));
foreach (var ctor in ctors)
{
var parameters = ctor.GetParameters();
if (parameters.Length > 0)
{
var keyParameter = parameters.First(x => x.Position == 0); // The first parameter has to be the key.
var matchingKeyProperty = keyParameter.Name.First().ToString().ToUpperInvariant()
+ keyParameter.Name.Substring(1); // proNumber -> ProNumber
var valueKeyInfo = typeof(V).GetProperty(matchingKeyProperty);
Debug.Assert(valueKeyInfo != null, string.Format("Expecting {0} to be key property for type {1}.", matchingKeyProperty, typeof(V)));
return valueKeyInfo;
}
}
throw new InvalidOperationException(string.Format("Could not find a value key for type {0}.", typeof(V)));
}
private static K GetKeyValue(V item)
{
var keyObj = _vKeyInfo.GetValue(item);
var keyValue = Activator.CreateInstance(typeof(K), keyObj);
return (K)keyValue;
}
private static Func<V, K> _autoGetKeySelector()
{
if (_vKeySelector != null) return _vKeySelector;
if (_vKeyInfo == null) _vKeyInfo = GetPropertyInfoOfValueKey();
_vKeySelector = x => GetKeyValue(x);
return _vKeySelector;
}
public TelaReactiveSourceCache()
: base(_autoGetKeySelector())
{
}
public TelaReactiveSourceCache(Func<V, K> keySelector)
: base(keySelector)
{
_vKeySelector = keySelector;
}
public Dictionary<K, V> ToDictionary()
{
var dict = this.KeyValues.ToDictionary(key => key.Key, value => value.Value);
return dict;
}
}
public class TelaReactiveSourceCacheSerializer : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
/// <summary>
/// We deserialize as a SourceCache<V, K>
/// </summary>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jsonObject = JObject.Load(reader);
Type[] genericTypes = objectType.GetGenericArguments();
Type myBaseListType = typeof(List<>);
Type myListType = myBaseListType.MakeGenericType(genericTypes[0]);
dynamic values = Activator.CreateInstance(myListType);
foreach (var property in jsonObject.Properties())
{
var jsonValue = property.First();
dynamic value = jsonValue.ToObject(genericTypes[0], serializer);
values.Add(value);
}
object sourceCache = existingValue;
if (existingValue == null)
{
sourceCache = Activator.CreateInstance(objectType);
}
var addMethod = typeof(ObservableCacheEx).GetMethods()
.Where(x => x.Name == "AddOrUpdate")
.ToList()[1]; // There a 2 methods with same name (TODO: weak code)
var addMethodGeneric = addMethod.MakeGenericMethod(genericTypes);
// Calling sourceCache.AddOrUpdate(values), i.e. ObservableCacheEx.AddOrUpdate(sourceCache, values)
addMethodGeneric.Invoke(sourceCache, new object[] { sourceCache, values }); // This is an extension method, so first parameter is source cache itself
return sourceCache;
}
/// <summary>
/// We generate JSON as a dictionary<K,V>
/// </summary>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var toSerialize = ((dynamic)value).ToDictionary();
serializer.Serialize(writer, toSerialize);
}
}
}
private TelaReactiveSourceCache<Trade, string> _tradesSource;
public TelaReactiveSourceCache<Trade, string> TradesSource
{
get { return _tradesSource; }
set { this.RaiseAndSetIfChanged(ref _tradesSource, value); }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment