Diff/Merge functionality for objects (not files!)
I have a collection of objects of the same type, let's call it DataItem
. The user can view and edit these items in an editor. It should also be possible to compare and merge different items, i.e. some sort of diff/merge for DataItem
instances.
The DIFF functionality should compare all (relevant) properties/fields of the items and detect possible differences. The MERGE functionality should then be able to merge two instances by applying selected differences to one of the items.
For example (pseudo objects):
DataItem1 { DataItem2 {
Prop1 = 10 Prop1 = 10
Prop2 = 25 Prop2 = 13
Prop3 = 0 Prop3 = 5
Coll = { 7, 4, 8 } Coll = { 7, 4, 8, 12 }
} }
Now, the user should be provided with a list of differences (i.e. Prop2
, Prop3
, and Coll
) and he should be able to select which differences he wants to eliminate by assigning the value from one item to the other. He should also be able to choose if he wants to assign the value from DataItem1
to DataItem2
or vice versa.
Are there common practices which should be used to implement this functionality?
Since the same editor should also provide undo/redo functionality (using the Command pattern), I was thinking about reusing the ICommand
implementations because both sc开发者_C百科enarios basically handle with property assignments, collection changes, and so on... My idea was to create Difference
objects with ICommand
properties which can be used to perform a merge operation for this specific Difference
.
Btw: The programming language will be C# with .NET 3.5SP1/4.0. However, I think this is more of a language-independent question. Any design pattern/idea/whatsoever is welcome!
That's pretty much what I do. I have a "Diff" class for an object that uses a PropertyDiff class to compare property values using Reflection. It's too much to paste all the code into SO but this should give you an idea. The collection of not-equal PropertyDiff objects is then displayed to the user who can pick which ones to keep or discard. We use NHibernate so the objects are changed in memory and then all the changes are persisted in a transaction. We did have an older version that built up a collection of SQL commands and that worked as well but you have to be careful that the commands are executed in the same order and in a transaction. The worst thing that can happen is if an exception occurs and both objects are FUBAR.
The PropertyDiff class represents a comparison between the same property on two objects. This works for simple properties only and there's separate code for maintaining collections.
public class PropertyDiff
{
private bool _isEqual;
public PropertyDiff(string propertyName, object xvalue, object yvalue)
{
PropertyName = propertyName;
Xvalue = xvalue;
Yvalue = yvalue;
_isEqual = Xvalue == Yvalue;
}
public string PropertyName { get; private set; }
public object Xvalue { get; private set; }
public object Yvalue { get; private set; }
public bool IsEqual
{
get { return _isEqual; }
}
internal static IList<PropertyDiff> GetPropertyDiffs(IEnumerable<string> properties, object x, object y)
{
if (x.GetType() != y.GetType())
{
throw new ArgumentException("Objects must be of same type");
}
var list = new List<PropertyDiff>();
var t = x.GetType();
foreach (string propertyName in properties)
{
PropertyInfo pi = t.GetProperty(propertyName);
if (pi != null)
{
object xVal = pi.GetValue(x, null);
object yVal = pi.GetValue(y, null);
PropertyDiff propDiff = new PropertyDiff(propertyName, xVal, yVal);
list.Add(propDiff);
}
}
return list;
}
}
And the CompanyDiff class:
public class CompanyDiff
{
private List<string> _propertyNames;
private IList<PropertyDiff> _propertyDiffs;
public CompanyDiff(Company companyX, Company companyY)
{
this.CompanyX = companyX;
this.CompanyY = companyY;
// Build list of property names to be checked
_propertyNames = new List<string>()
{
"Type",
"Name",
"DBA"
// etc.
};
_propertyDiffs = PropertyDiff.GetPropertyDiffs(_propertyNames, this.CompanyX, this.CompanyY);
}
public Company CompanyX { get; private set; }
public Company CompanyY { get; private set; }
public IList<PropertyDiff> PropertyDiffs
{
get { return _propertyDiffs; }
}
}
精彩评论