private IEnumerable <string> GetPathArray <TSlice>(IStoreAction <TState, TSlice> action) where TSlice : class, new() { var expression = action.GetActionPath(); var pathArray = expression.Body.ToString().Split('.').Skip(1); return(pathArray); }
private void EnsureValidAction <TSlice>(IStoreAction <TState, TSlice> action) where TSlice : class, new() { if (action == null) { throw new ArgumentException($"Action can not be null"); } }
private string GetPath <TSlice>(IStoreAction <TState, TSlice> action) where TSlice : class, new() { IEnumerable <string> pathArray = GetPathArray(action); var path = string.Join(".", pathArray); return(path); }
/// <summary> /// Subscribe to a specifc slice of the global state /// </summary> /// <typeparam name="TSlice">The type of the slice</typeparam> /// <param name="action">The action responsible for the slice</param> /// <param name="callback">The callback perfomerd when the slice is changed</param> public IStorageSubscription Subscribe <TSlice>(IStoreAction <TState, TSlice> action, Action <TSlice> callback) where TSlice : class, new() { EnsureValidAction(action); var actionPath = this.GetPath(action); if (string.IsNullOrWhiteSpace(actionPath)) { maincallbacks.Add(callback); return(new StorageSubscription(maincallbacks, callback)); } if (callbacks.ContainsKey(actionPath)) { var list = callbacks[actionPath]; list.Add(callback); return(new StorageSubscription(list, callback)); } else { var list = new List <Delegate>(); list.Add(callback); callbacks.Add(actionPath, list); return(new StorageSubscription(list, callback)); } }
/// <summary> /// Selects a specific slice of the global state /// </summary> /// <typeparam name="TSlice">The type of the slice</typeparam> /// <param name="action">The action responsible for the slice</param> /// <returns></returns> public TSlice Select <TSlice>(IStoreAction <TState, TSlice> action) where TSlice : class, new() { EnsureValidAction(action); // Gets the Object that will be changed object currentObject = GetStoreObjectByPath(this.state, action); return(this.mapper.Clone <TSlice>(currentObject)); }
/// <summary> /// Sets a specific slice of the global state /// </summary> /// <typeparam name="TSlice">The type of the slice that will be changed</typeparam> /// <param name="action">The action responsible for the slice</param> /// <param name="slice">The new state to be dispatched</param> public void SetState <TSlice>(IStoreAction <TState, TSlice> action, TSlice slice) where TSlice : class, new() { // Ensures that the action is valid EnsureValidAction(action); var actionPath = this.GetPath(action); var actionName = action?.GetActionName(); // Copies the new state of the store var newState = this.mapper.Clone(this.state); // Gets the Object that will be changed object newSlice = GetStoreObjectByPath(newState, action); // Maps the new state to the object that will be changed mapper.Map(slice, newSlice); // Adds the state history var stateEntry = new StateEntry <TState> { ActionPath = actionPath, ActionName = actionName, OldState = this.state, NewState = newState, UpdatedAt = DateTimeOffset.Now }; stateHistory.Add(stateEntry); // Changes the store to the new state this.state = newState; // Notifies the main subscribers foreach (var callback in maincallbacks) { callback.DynamicInvoke(this.state); } // Notifies the subscribers to child states if (actionPath != null && callbacks.ContainsKey(actionPath)) { foreach (var callback in callbacks[actionPath]) { callback.DynamicInvoke(newSlice); } } // Logs the history if (isLogEnabled) { logging.Log($"*** State changed on {typeof(TState).Name} ***"); logging.Log(jsonService.Serialize(stateEntry)); } // Notifies that the state was changed //this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.State))); }
private object GetStoreObjectByPath <TSlice>(TState newState, IStoreAction <TState, TSlice> action) where TSlice : class, new() { object currentObject = newState; var pathArray = this.GetPathArray(action); foreach (var pathItem in pathArray) { var prop = currentObject.GetType().GetProperty(pathItem); var newObject = prop.GetValue(currentObject); if (newObject == null) { newObject = Activator.CreateInstance(prop.PropertyType); prop.SetValue(currentObject, newObject); } currentObject = newObject; } return(currentObject); }