Requirement is to monitor the changes in a List<T>
, possible changes are Add / Remove / Update
, which are registered in an Audit log, which I do in the code underneath using a Dictionary
. Now user can take an action to revert each of the operations, using an integrated Action
delegate. Each operation runs its respective Revert operation. Please note here undo is not about notification, like ObservableCollection<T>
but removal at the later time based on user discretion.
Following is my design, please suggest, what shall be done to further improvise.
public class ActionWrapper<T>
{
public int Index {get;set;}
public T OriginalValue {get;set;}
public T NewValue {get;set;}
public Action<int,T> Action {get;set;}
}
public class ChangeList<T> : List<T>
where T:class,IEquatable<T>
{
public Dictionary<T,ActionWrapper<T>> ActionMap {get;set;}
public ChangeList()
{
ActionMap = new Dictionary<T,ActionWrapper<T>>();
}
public new void Add(T item)
{
base.Add(item);
var actionWrapper = new ActionWrapper<T>
{
Action = new Action<int,T>(RevertAdd),
Index = this.FindIndex(x => x.Equals(item)),
NewValue = item,
OriginalValue = null
};
ActionMap[actionWrapper.NewValue] = actionWrapper;
}
public new void Remove(T item)
{
var actionWrapper = new ActionWrapper<T>
{
Action = new Action<int, T>(RevertRemove),
Index = this.FindIndex(x => x.Equals(item)),
NewValue = null,
OriginalValue = item
};
if(actionWrapper.Index < 0)
return;
base.Remove(actionWrapper.OriginalValue);
ActionMap[actionWrapper.OriginalValue] = actionWrapper;
}
public void Update(T actualValue,T newValue)
{
var actionWrapper = new ActionWrapper<T>
{
Action = new Action<int, T>(RevertUpdate),
Index = this.FindIndex(x => x.Equals(actualValue)),
NewValue = newValue,
OriginalValue = actualValue
};
if (actionWrapper.Index < 0)
return;
base[actionWrapper.Index] = newValue;
ActionMap[actionWrapper.NewValue] = actionWrapper;
}
public void RevertAdd(int index, T actual)
{
base.Remove(actual);
}
public void RevertRemove(int index,T actual)
{
base.Add(actual);
}
public void RevertUpdate(int index,T actual)
{
base[index] = actual;
}
}
Use Case
void Main()
{
var changeList = new ChangeList<string>();
changeList.Add("Person1");
changeList.Add("Person2");
changeList.Add("Person3");
changeList.Add("Person4");
changeList.Add("Person5");
changeList.Add("Person6");
changeList.Add("Person7");
changeList.Dump(); // Print statement
var actionMapUpdateAdd = changeList.ActionMap["Person5"];
actionMapUpdateAdd.Action(actionMapUpdateAdd.Index, actionMapUpdateAdd.NewValue);
changeList.Dump(); // Print statement
changeList.Update("Person7","Person77");
changeList.Dump(); // Print statement
var actionMapUpdate = changeList.ActionMap["Person77"];
actionMapUpdate.Action(actionMapUpdate.Index,actionMapUpdate.OriginalValue);
changeList.Dump(); // Print statement
changeList.Remove("Person6");
changeList.Dump(); // Print statement
var actionMapRemove = changeList.ActionMap["Person6"];
actionMapRemove.Action(actionMapRemove.Index, actionMapRemove.OriginalValue);
changeList.Dump(); // Print statement
}
ActionMap
as a public property with a public setter is just asking for problems, for that having a method recovering theActionWrapper
should greatly reduce the risk there of user manipulation \$\endgroup\$