问题
I have a form where users can modify a collection of objects using a DataGrid. When the form is opened I create a deep copy of the original collection and if the Cancel button is pressed I just discard that copy.
The problem is that when the OK button is pressed I have to reconcile the changes which might be:
- Modified properties of existing objects
- New objects added to any place in the collection.
- Existing objects removed.
- Existing objects re-ordered.
Since I need to keep the original references I can't just clear the collection and add the modified items.
Do you know a simple algorithm that would synchronize two collections like this?
I'm using C# 3.5 so LINQ is available.
回答1:
I just worked out the following algorithm for a project I'm working on and I think it complies with your requirements. It follows this steps:
- Creates a shallow copy of the original list.
- Clears the original list.
- Foreach item in the modified list.
- If it finds the original object adds it to the list and updates it's properties.
- If not just add it to the list.
This method maintains the new order and reuses the original references but requires that your objects have an unique identifier. I'm using a Guid for that.
var originalPersons = m_originalList.ToList();
m_originalList.Clear();
foreach (var modifiedPerson in m_modifiedList)
{
var originalPerson = originalPersons.FirstOrDefault(c => c.ID == modifiedPerson.ID);
if (originalPerson == null)
m_originalList.Add(modifiedPerson);
else
{
m_originalList.Add(originalPerson);
originalPerson.Document = modifiedPerson.Document;
originalPerson.Name = modifiedPerson.Name;
...
}
}
Good luck.
回答2:
Why not keep a copy of the original, and bind to the actual collection. If you bind to the actual collection, then there is no reconciliation. When cancel is pressed, simply replace the actual collection with the copy of the original. It is basically an inversion of what you are trying to do...but it should be a lot easier to manage.
回答3:
The way I've dealt with this depends on the collections of objects having a unique ID. I also pass in the repository to deal with this as well, but for brevity I left it out. It's similiar to the following:
public static void MergeFields(IEnumerable<TYPE1> persistedValues, IEnumerable<TYPE1> newValues)
{
var leftIds = persistedValues.Select(x => x.Id).ToArray();
var rightIds = newValues.Select(x => x.Id).ToArray();
var toUpdate = rightIds.Intersect(leftIds).ToArray();
var toAdd = rightIds.Except(leftIds).ToArray();
var toDelete = leftIds.Except(rightIds).ToArray();
foreach(var id in toUpdate){
var leftScope = persistedValues.Single(x => x.ID == id);
var rightScope = newValues.Single(x => x.ID == id);
// Map appropriate values from rightScope over to leftScope
}
foreach(var id in toAdd) {
var rightScope = newValues.Single(x => x.ID == id);
// Add rightScope to the repository
}
foreach(var id in toDelete) {
var leftScope = persistedValues.Single(x => x.ID == id);
// Remove leftScope from the repository
}
// persist the repository
}
来源:https://stackoverflow.com/questions/1644914/reconciling-two-collections-of-objects