Closed kenperregaux closed 3 years ago
@kenperregaux behavior you described is correct. Comparer compare individual elements of the list only if lists have the same number of elements.
You can write your custom comparer to compare elements. This example compare elements based on ids.
More details why it's not implemented yet here
I'm going to implement some custom list comparers in the next version.
Thanks for the quick response. Will take a look at the example!
I am running your test code right now and have a couple questions.
@kenperregaux can you share your code?
` internal class TestFactory : ComparersFactory
{
public override ObjectsComparer.IComparer
var comparer = new CustomFormulaItemsComparer(settings, parentComparer, this);
return (ObjectsComparer.IComparer<T>)comparer;
}
}
internal class CustomFormulaItemsComparer : AbstractComparer<IList<FormulaItem>>
{
public CustomFormulaItemsComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory) : base(settings, parentComparer, factory)
{
}
public override IEnumerable<Difference> CalculateDifferences(IList<FormulaItem> obj1, IList<FormulaItem> obj2)
{
if (obj1 == null && obj2 == null)
{
yield break;
}
if (obj1 == null || obj2 == null)
{
yield return new Difference("", DefaultValueComparer.ToString(obj1), DefaultValueComparer.ToString(obj2));
yield break;
}
if (obj1.Count != obj2.Count)
{
yield return new Difference("Count", obj1.Count.ToString(), obj2.Count.ToString(),
DifferenceTypes.NumberOfElementsMismatch);
}
var list2 = obj2.ToList();
var ids = new List<long>();
foreach (var formulaItem in obj1)
{
var formulaItem2 = list2.FirstOrDefault(fi => fi.Id == formulaItem.Id);
if (formulaItem2 == null)
{
formulaItem2 = new FormulaItem();
}
else
{
ids.Add(formulaItem.Id);
}
var comparer = Factory.GetObjectsComparer<FormulaItem>();
foreach (var difference in comparer.CalculateDifferences(formulaItem, formulaItem2))
{
yield return difference.InsertPath($"[Id={formulaItem.Id}]");
}
}
var formulatItem1 = new FormulaItem();
foreach (var formulaItem2 in obj2)
{
if (!ids.Contains(formulaItem2.Id))
{
var comparer = Factory.GetObjectsComparer<FormulaItem>();
foreach (var difference in comparer.CalculateDifferences(formulatItem1, formulaItem2))
{
yield return difference.InsertPath($"[Id={formulaItem2.Id}]");
}
}
}
}
}
internal class Formula
{
public long Id { get; set; }
public string Name { get; set; }
public IList<FormulaItem> Items { get; set; }
}
internal class FormulaItem
{
public long Id { get; set; }
public int Delay { get; set; }
public string Name { get; set; }
public string Instruction { get; set; }
}
// Then to test I used...
var tf = new TestFactory();
var tc = tf.GetObjectsComparer
The code works because in tds I see the differences. But I was confused because I couldn't set a break point.
Part of my understanding problem is the GetObjectsComparer of the factory only gets called once for the Formula (I would have thought it would be called twice once for Formula and once for IList
Also, I added a simple dictionary to the Formula class so I assumed I would need another converter...so I added one but doesn't seem to get called...here is my factory code now...is this wrong?
internal class TestFactory : ComparersFactory
{
public override ObjectsComparer.IComparer<T> GetObjectsComparer<T>(ComparisonSettings settings = null,
BaseComparer parentComparer = null)
{
if (typeof(T) == typeof(IList<FormulaItem>))
{
return (ObjectsComparer.IComparer<T>)new CustomFormulaItemsComparer(settings, parentComparer, this);
}
if (typeof(T) == typeof(Dictionary<string, string>))
{
return (ObjectsComparer.IComparer<T>)new CustomElementsComparer(settings, parentComparer, this);
}
return base.GetObjectsComparer<T>(settings, parentComparer);
}
}
1) Probably condition typeof(T) == typeof(IList
P.S. Can you please fix code formatting, it's difficult to read?
"Part of my understanding problem is the GetObjectsComparer of the factory only gets called once for the Formula (I would have thought it would be called twice once for Formula and once for IList)"
Yes. It should be called twice.
Hi I seemed to have solve most of my problems, thank so much for the help. I do have one more question. I have tried to use the IgnoreMember api on a comparer but it is not defined and I can't seem to find it anywhere in the definitions of your classes or interfaces. Thanks again!
@kenperregaux, what exactly "not defined"? https://github.com/ValeraT1982/ObjectsComparer#ignoring-members describes how to use it.
var comparer = factory.GetObjectsComparer
Getting the error:
'IComparer
So I was using it (or so I thought) exactly how the example shows.
IgnoreMember method are part of BaseComparer. I need to add them to IBaseComparer also, I'll fix it in the next version. The simplest workaround is to use AddComparerOverride methods and use DoNotCompareValueComparer as comparer.
Thank you for finding this issue!!!
Ah OK thanks!
Sorry but I still have one more question, I want to show my users the full object when a new item is added to a collection. Giving the comparer a new of the object doesn't work because some properties are the defaults. Is there a way to create a custom comparer which loops through all the properties in the object and returns a defference with MissedElementInFirstObject difference type?
@kenperregaux. no worries 😉 . The only way to do it is to implement it in your custom comparer. I don't see any existing comparer pieces that can help you with this.
I am investigating this package and and was very excited about it. However, I am running into a bit of a road block and would love some help. Below are the classes and test data I am using to test with and as you can see the main class has a list of a sub class. However, the only differences I get are the name difference if there is a difference in length of lists...I was hoping it would return the name difference, the difference in Val1 of the second item in the list and the complete additional object from the list in the second main object. Any help on this would be awesome, thanks in advance. The funny thing is if I comment out the 3rd element in the list of test2 then I do get the difference of the 2nd element in the list.
// Classes internal class TestSub { public string Val1 { get; set; } public int Val2 { get; set; } }; internal class TestMain { public string Name { get; set; } public List TestSubs { get; set; }
};
// Test data and differences run var test1 = new TestMain { Name = "Ken", TestSubs = new List
{
new TestSub { Val1 = "val1", Val2 = 1 },
new TestSub { Val1 = "val2", Val2 = 2 }
}
};
var test2 = new TestMain
{
Name = "KenP",
TestSubs = new List
{
new TestSub { Val1 = "val1", Val2 = 1 },
new TestSub { Val1 = "val22", Val2 = 2 },
new TestSub { Val1 = "val3", Val2 = 3 }
}
};
var testComparer = new ObjectsComparer.Comparer();
IEnumerable testDiffs;
var t = testComparer.Compare(test1, test2, out testDiffs);