/// <summary> /// Updates the object value from a source object of the same type. /// </summary> /// <param name="loader"></param> /// <param name="source"></param> private void UpdateFrom(ValueLoader loader, object source) { Version++; // if no one is holding the value, don't bother updating. // if (!CheckIfAnyoneCares()) { return; } int version = Version; object value = ValueInternal; // sure source matches dest // if (!value.GetType().IsInstanceOfType(source)) { throw new InvalidOperationException("Types not compatible"); } Action handler = () => { // make sure another update hasn't beat us to the punch. if (Version > version) { return; } try { _stats.OnStartUpdate(); ReflectionSerializer.UpdateObject(source, value, true, LastUpdatedTime); } finally { _stats.OnCompleteUpdate(); } // notify successful completion. NotifyCompletion(loader, null); }; if (SynchronousMode) { handler(); } else { PriorityQueue.AddUiWorkItem( handler ); } }
private CacheEntry Get <T>(LoadContext loadContext, Action <T> completed, Action <Exception> error, bool resetCallbacks) where T : new() { if (loadContext == null) { throw new ArgumentNullException("LoadContext required."); } object identity = loadContext.UniqueKey; CacheEntry value; lock (_objectCache) { if (_objectCache.ContainsKey(typeof(T)) && _objectCache[typeof(T)].TryGetValue(identity, out value)) { value.LoadContext = loadContext; if (resetCallbacks) { SetupCompletedCallback <T>(completed, error, value); } return(value); } } Type objectType = typeof(T); Action <CacheEntry> proxyCallback = cacheEntry => { var v = (T)cacheEntry.ValueInternal; foreach (var proxy in GetProxies <T>(cacheEntry.LoadContext)) { // copy the values over // ReflectionSerializer.UpdateObject(v, proxy.ProxyReference.Target, true, null); // fire the update notification // if (proxy.UpdateAction != null) { proxy.UpdateAction(); } } }; // create a new one. // value = new CacheEntry(loadContext, objectType, proxyCallback); value.NextCompletedAction.UnhandledError = OnUnhandledError; object loader = GetDataLoader(value); // How to create a new value. It's just a new. // value.CreateDefaultAction = () => { // if there is a proxy already registered, use it as the key value. // var proxy = GetProxies <T>(loadContext).FirstOrDefault(); if (proxy != null && proxy.ProxyReference != null && proxy.ProxyReference.IsAlive) { return(proxy.ProxyReference.Target); } var item = new T(); if (item is ILoadContextItem) { ((ILoadContextItem)item).LoadContext = value.LoadContext; } return(item); }; SetupCompletedCallback <T>(completed, error, value); // How to load a new value. // value.LoadAction = (lvl) => { if (loader == null) { throw new InvalidOperationException("Could not find loader for type: " + typeof(T).Name); } // get a loader and ask for a load request. // Debug.Assert(loader != null, "Failed to get loader for " + typeof(T).Name); var request = DataLoaderProxy.GetLoadRequest(loader, value.LoadContext, typeof(T)); if (request == null) { Debug.WriteLine("{0}: Aborting load for {1}, ID={2}, because {3}.GetLoadRequest returned null.", DateTime.Now, typeof(T).Name, value.LoadContext.Identity, loader.GetType().Name); return(false); } // fire off the load. // IsLoading = true; request.Execute( (result) => { if (result == null) { throw new ArgumentNullException("result", "Execute must return a LoadRequestResult value."); } if (result.Error == null) { lvl.OnLoadSuccess(result.Stream); } else { lvl.OnLoadFail(new LoadRequestFailedException(lvl.CacheEntry.ObjectType, value.LoadContext, result.Error)); } IsLoading = false; } ); return(true); }; // how to deserialize. value.DeserializeAction = (id, data, isOptimized) => { if (loader == null) { throw new InvalidOperationException("Could not find loader for type: " + typeof(T).Name); } // get the loader and ask for deserialization // object deserializedObject = null; if (isOptimized) { var idl = (IDataOptimizer)loader; if (idl == null) { throw new InvalidOperationException("Data is optimized but object does not implmenent IDataOptimizer"); } deserializedObject = idl.DeserializeOptimizedData(value.LoadContext, objectType, data); } else { deserializedObject = DataLoaderProxy.Deserialize(loader, value.LoadContext, objectType, data); } if (deserializedObject == null) { throw new InvalidOperationException(String.Format("Deserialize returned null for {0}, id='{1}'", objectType.Name, id)); } if (!objectType.IsInstanceOfType(deserializedObject)) { throw new InvalidOperationException(String.Format("Returned object is {0} when {1} was expected", deserializedObject.GetType().Name, objectType.Name)); } return(deserializedObject); }; // if this thing knows how to optimize, hook that up. // if (loader is IDataOptimizer) { value.SerializeOptimizedDataAction = (obj, stream) => { var idl = (IDataOptimizer)loader; return(idl.SerializeOptimizedData(obj, stream)); }; } // finally push the value into the cache. lock (_objectCache) { if (!_objectCache.ContainsKey(objectType)) { Dictionary <object, CacheEntry> typeDictionary = new Dictionary <object, CacheEntry>(); _objectCache[typeof(T)] = typeDictionary; } _objectCache[typeof(T)][identity] = value; } return(value); }
/// <summary> /// Merge a list of items with the contents of this ObsColl. /// /// IMPORTANT: This method assumes that the contents of the this list are sorted /// in the same sort order as specified by the compare action. /// /// If they are not, then the resulting list is likely to be wrong. /// </summary> /// <param name="newItems">The new list of ites</param> /// <param name="compare">The comparison function between two items</param> /// <param name="itemMergeBehavior">The way toi handle equivlent items.</param> public void Merge(IList <T> newItems, Comparison <T> compare, EquivelentItemMergeBehavior itemMergeBehavior) { // Shortcuts for 0 items // if (newItems == null || newItems.Count == 0) { ClearItems(); return; } if (Count == 0) { AddRange(newItems); return; } CancelBatch(); var sortedExisting = this; // we have to assume the list is currently sorted by the specified comparer. // sort the newArray var sortedNew = newItems.ToArray(); Array.Sort <T>(sortedNew, compare); int currentPos = 0; // Now walk each item in the new array and compare it against the current // items in the collection. // foreach (var newItem in sortedNew) { T existingItem = default(T); // if we're past the end of the old list, // just start adding. // if (currentPos < Count) { existingItem = this[currentPos]; } else { Add(newItem); continue; } int compareResult = compare(newItem, existingItem); if (compareResult == 0) { // we found the match, so just replace the item // or do nothing. // bool isSameObject = Object.Equals(existingItem, newItem); if (isSameObject) { switch (itemMergeBehavior) { case EquivelentItemMergeBehavior.ReplaceEqualItems: this[currentPos] = newItem; break; case EquivelentItemMergeBehavior.UpdateEqualItems: ReflectionSerializer.UpdateObject(newItem, existingItem, true, null); break; default: break; } } else { // TODO: WRITE TEST FOR THIS CASE // something compared as equal, but it's a different object // so insert the new one before the existing one. // this.Insert(currentPos, newItem); } currentPos++; } else if (compareResult < 0) { // the new item comes before the existing item, so add it. // this.Insert(currentPos, newItem); currentPos++; } else if (compareResult > 0) { // the new item should come after this item, // so just replace the current item with our new item. // this[currentPos] = newItem; currentPos++; } } // remove any that are left in the old list. // for (int i = this.Count - 1; i >= currentPos; i--) { this.RemoveAt(i); } }