Created
February 27, 2016 18:59
-
-
Save ericdes/caa82ce357c98ce8f4cb to your computer and use it in GitHub Desktop.
TelaReactiveSourceCache
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private void LoadFromJson_Click(object sender, RoutedEventArgs e) | |
{ | |
if (_json == null) return; | |
var myTrades = JsonConvert.DeserializeObject<TelaReactiveSourceCache<Trade, string>>(_json); | |
ViewModel.TradesSource = myTrades; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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