Ну, я думаю, что консолидация операций - лучший доступный вариант, и в моем случае я не мог придумать сценарий, в котором было бы создано неоптимальное решение.Если кому-то действительно интересно, я поделился своим кодом консолидации здесь:
public class TreeOperationConsolidator : ITreeOperationConsolidator
{
public IEnumerable<ITreeOperation> ConsolidateOperations(IEnumerable<ITreeOperation> operations)
{
List<ITreeOperation> result = new List<ITreeOperation>();
foreach (var op in operations)
{
if (op.Operation == OperationType.Move)
{
ConsolidateMoveOperation(op, operations, result);
}
else if (op.Operation == OperationType.Rename)
{
ConsolidateRenameOperation(op, operations, result);
}
else if (op.Operation == OperationType.Create)
{
ConsolidateCreateOperation(op, operations, result);
}
else if (op.Operation == OperationType.Delete)
{
ConsolidateDeleteOperation(op, operations, result);
}
}
return result;
}
private void ConsolidateDeleteOperation(ITreeOperation op, IEnumerable<ITreeOperation> operations, List<ITreeOperation> result)
{
bool newlyCreated = result.Any(o => o.SourceId == op.SourceId && o.Operation == OperationType.Create);
result.RemoveAll(o => o.SourceId == op.SourceId);
var children = (from o in result
where
(o.Operation == OperationType.Move && o.DestId == op.SourceId)
|| (o.Operation == OperationType.Create && o.DestId == op.SourceId)
select o).ToList();
foreach (var child in children)
{
result.Remove(child);
ConsolidateDeleteOperation(new TreeOperation { Operation = OperationType.Temp, SourceId = child.SourceId }, operations, result);
}
if (newlyCreated == false && op.Operation != OperationType.Temp)
result.Add(op);
}
private void ConsolidateCreateOperation(ITreeOperation op, IEnumerable<ITreeOperation> operations, List<ITreeOperation> result)
{
result.Add(op);
}
private void ConsolidateRenameOperation(ITreeOperation op, IEnumerable<ITreeOperation> operations, List<ITreeOperation> result)
{
var createOperation = result.FirstOrDefault(o => o.SourceId == op.SourceId && o.Operation == OperationType.Create);
if (createOperation == null)
{
var renameOp = result.FirstOrDefault(o => o.SourceId == op.SourceId && o.Operation == op.Operation);
if (renameOp != null)
{
result.Remove(renameOp);
}
result.Add(op);
}
else
{
createOperation.Argument = op.Argument;
}
}
protected void ConsolidateMoveOperation(ITreeOperation op, IEnumerable<ITreeOperation> operations, List<ITreeOperation> result)
{
var createOperation = result.FirstOrDefault(o => o.SourceId == op.SourceId && o.Operation == OperationType.Create);
if (createOperation == null)
{
var moveOp = result.FirstOrDefault(o => o.SourceId == op.SourceId && o.Operation == op.Operation);
if (moveOp != null)
{
result.Remove(moveOp);
}
result.Add(op);
}
else
{
createOperation.DestId = op.DestId;
}
}
}
public class TreeOperation : ITreeOperation
{
public string Argument { get; set; }
public OperationType Operation { get; set; }
public string SourceId { get; set; }
public string DestId { get; set; }
}
public enum OperationType
{
Move,
Rename,
Create,
Delete,
Temp
}
public interface ITreeOperationConsolidator
{
IEnumerable<ITreeOperation> ConsolidateOperations(IEnumerable<ITreeOperation> operations);
}
public interface ITreeOperation
{
string Argument { get; set; }
OperationType Operation { get; set; }
string SourceId { get; set; }
string DestId { get; set; }
}
Так что вам нужно отслеживать все действия пользователя в дереве (т.е. сохранять экземпляры ITreeOperation в сеансе (или где-то еще). Перед применением всех изменений обязательно позвоните IEnumerable<ITreeOperation> ConsolidateOperations(IEnumerable<ITreeOperation> operations)
.