Let's say i have such structure
public class Form
{
#region Public Properties
public List<Field> Fields { get; set; }
public string Id { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public int Revision { get; set; }
#endregion
}
So the Form
class contains the list of fields, let's say that the Field itself is represented with such structure
public class Field
{
#region Public Properties
public string DisplayName { get; set; }
public List<Field> Fields { get; set; }
public string Id { get; set; }
public FieldKind Type { get; set; }
public FieldType FieldType { get; set; }
#endregion
}
The Field
is basically composite structure, each Field
contains the list of child fields. So the structure is hierarchical. Also Field has reference to the FieldType
class.
public class FieldType
{
#region Public Properties
public DataType DataType { get; set; }
public string DisplayName { get; set; }
public string Id { get; set; }
#endregion
}
And at the end we have reference to the DataType
public class DataType
{
#region Public Properties
public string BaseType { get; set; }
public string Id { get; set; }
public string Name { get; set; }
public List<Restriction> Restrictions { get; set; }
#endregion
}
What I want to achieve is to get the difference of such complex structure, let's say if I have some sort of Comparer it will give me structured difference for the whole form as one class, let's say DifferenceResult. When I said structured difference I mean that it should be something like that.
- Difference for the
Form
fields (outputForm
has difference in Version field (different color) - Differences for the
Fields
collection, including the hierarchy of the fields to the edited field - Same behavior for the
FieldType
andDataType
- Detect removing and adding the
Field
into theForm
(so probably each Difference will have a Type)
What I have right now. I started with generic approach and tried to use ReflectionComparer implementing IEqualityComparer interface
public bool Equals(T x, T y)
{
var type = typeof(T);
if (typeof(IEquatable<T>).IsAssignableFrom(type))
{
return EqualityComparer<T>.Default.Equals(x, y);
}
var enumerableType = type.GetInterface(typeof(IEnumerable<>).FullName);
if (enumerableType != null)
{
var elementType = enumerableType.GetGenericArguments()[0];
var elementComparerType = typeof(DifferenceComparer<>).MakeGenericType(elementType);
var elementComparer = Activator.CreateInstance(elementComparerType, new object[] { _foundDifferenceCallback, _existedDifference });
return (bool)typeof(Enumerable).GetGenericMethod("SequenceEqual", new[] { typeof(IEnumerable<>), typeof(IEnumerable<>), typeof(IEqualityComparer<>) }).MakeGenericMethod(elementType).Invoke(null, new[] { x, y, elementComparer });
}
foreach (var propertyInfo in type.GetProperties())
{
var leftValue = propertyInfo.GetValue(x);
var rightValue = propertyInfo.GetValue(y);
if (leftValue != null)
{
var propertyComparerType = typeof(DifferenceComparer<>).MakeGenericType(propertyInfo.PropertyType);
var propertyComparer = Activator.CreateInstance(propertyComparerType, new object[] {_foundDifferenceCallback, _existedDifference});
if (!((bool)typeof(IEqualityComparer<>).MakeGenericType(propertyInfo.PropertyType)
.GetMethod("Equals")
.Invoke(propertyComparer, new object[] { leftValue, rightValue })))
{
// Create and publish the difference with its Type
}
}
else
{
if (!Equals(leftValue, rightValue))
{
// Create and publish the difference with its Type
}
}
}
//return true if no differences are found
}
And Difference
class
public struct Difference
{
public readonly string Property;
public readonly DifferenceType Type;
public readonly IExtractionable Expected;
public readonly IExtractionable Actual;
}
But probably it's not the way I want to go, because I should compare Field more precisely taking in consideration that each Field has Id, which can be different for different forms as well and I want to take more control of the comparing process. For me it sounds more like a diff tool.
So I am looking for good pattern and pretty good structure to publish the Difference to the client code, which can easily visualize it?