ValeraT1982 / ObjectsComparer

C# Framework provides mechanism to compare complex objects, allows to override comparison rules for specific properties and types.
MIT License
354 stars 87 forks source link

Problems comparing lists different size and comparing dates #3

Closed Edimarcos-Maranhao closed 6 years ago

Edimarcos-Maranhao commented 6 years ago

1 - I can not get differences from class members by comparing two lists where the quantity of items are different, when the lists are equal I get the differences. 2 - Dates equal get differences.

Example of model:

public class Formula
{
    public long Id { get; set; }
    public string Name { get; set; }
    public string Initials { get; set; }
    public DateTime CreationDate { get; set; }
    public IList<FormulaItem> Items { get; set; }
}

public class FormulaItem
{
    public long Id { get; set; }
    public int Delay { get; set; }
    public string Name { get; set; }
    public string Instruction { get; set; }
}

Example of Implementation of the tests:

[Test]
public void List_Of_Equal_Sizes_But_Is_Inequality()
{
    var formula1 = new Formula
    {
        Id = 1,
        Name = "Aenean Lorem Nibh",
        Initials = "ALN",
        CreationDate = DateTime.Now,
        Items = new List<FormulaItem>
        {
            new FormulaItem
            {
                Id = 4,
                Delay = 60,
                Name = "Duis et rhoncus risus",
                Instruction = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
            }
        }
    };

    var formula2 = new Formula
    {
        Id = 1,
        Name = "Aenean Quisque Nibh",
        Initials = "AQN",
        CreationDate = DateTime.Now,
        Items = new List<FormulaItem>
        {
            new FormulaItem
            {
                Id = 4,
                Delay = 30,
                Name = "Nulla ac vulputate magna",
                Instruction = "Phasellus nisl tortor, malesuada eget elit id, finibus volutpat est"
            }
        }
    };

    var comparer = new Comparer<Formula>();

    var isEqual = comparer.Compare(formula1, formula2, out var differences);

    Assert.IsTrue(!isEqual);
}

Result test: List_Of_Equal_Sizes_But_Is_Inequality

DifferenceType=ValueMismatch, MemberPath='Name', Value1='Aenean Lorem Nibh', Value2='Aenean Quisque Nibh' DifferenceType=ValueMismatch, MemberPath='Initials', Value1='ALN', Value2='AQN'

Equal date returned difference

DifferenceType=ValueMismatch, MemberPath='CreationDate', Value1='06/10/2018 18:04:57', Value2='06/10/2018 18:04:57'

DifferenceType=ValueMismatch, MemberPath='Items[0].Delay', Value1='60', Value2='30' DifferenceType=ValueMismatch, MemberPath='Items[0].Name', Value1='Duis et rhoncus risus', Value2='Nulla ac vulputate magna' DifferenceType=ValueMismatch, MemberPath='Items[0].Instruction', Value1='Lorem ipsum dolor sit amet, consectetur adipiscing elit', Value2='Phasellus nisl tortor, malesuada eget elit id, finibus volutpat est'

[Test]
public void List_Of_Different_Sizes_But_Is_Inequality()
{
    var formula1 = new Formula
    {
        Id = 1,
        Name = "Aenean Lorem Nibh",
        Initials = "ALN",
        CreationDate = DateTime.Now,
        Items = new List<FormulaItem>
        {
            new FormulaItem
            {
                Id = 4,
                Delay = 60,
                Name = "Duis et rhoncus risus",
                Instruction = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
            }
        }
    };

    var formula2 = new Formula
    {
        Id = 1,
        Name = "Aenean Quisque Nibh",
        Initials = "AQN",
        CreationDate = DateTime.Now,
        Items = new List<FormulaItem>
        {
            new FormulaItem
            {
                Id = 4,
                Delay = 60,
                Name = "Duis et rhoncus risus",
                Instruction = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
            },
            new FormulaItem
            {
                Id = 6,
                Delay = 30,
                Name = "Nulla ac vulputate magna",
                Instruction = "Phasellus nisl tortor, malesuada eget elit id, finibus volutpat est"
            }
        }
    };

    var comparer = new Comparer<Formula>();

    var isEqual = comparer.Compare(formula1, formula2, out var differences);

    Assert.IsTrue(!isEqual);
}

Result test: List_Of_Different_Sizes_But_Is_Inequality

DifferenceType=ValueMismatch, MemberPath='Name', Value1='Aenean Lorem Nibh', Value2='Aenean Quisque Nibh' DifferenceType=ValueMismatch, MemberPath='Initials', Value1='ALN', Value2='AQN' DifferenceType=ValueMismatch, MemberPath='CreationDate', Value1='06/10/2018 18:01:27', Value2='06/10/2018 18:01:27'

Case I use List and not IList in the Items property I get only one difference that refers to the count in the list

DifferenceType=ValueMismatch, MemberPath='Items.Count', Value1='1', Value2='2'

Is there something I'm not implementing or would it be a bug? I use the net461 framework in my tests.

ValeraT1982 commented 6 years ago

Hi @Edimarcos. Thanks for you question. Behavior you described is correct. If the numbers of elements are not the same Comparer doesn't compare elements. This situations can be tricky because:

  1. How to match elements between the lists? In your example should Comparer compare first elements of formula1.Items with the first element of the formula2.Items? Or with the second? In your case, as I understand, Id property can be used to match elements, but how Comparer should know about it?
  2. In majority of the use cases the purpose of the Comparer it to say if objects are equal or not. Comparing all elements in the list (it could be hundreds, thousands or even more... field by field) can take a lot of time. And Comparer already know that objects are different....
  3. What if objects are the same, but order are different?
  4. What if there are more than one item with the same Id?

In your case the best way would be to implement custom Comparer (it should implement IComparer<IList>) and implement your logic of comparing lists there. Then you need to implement ComparersFactory (see Example 2: Persons comparison) to return your custom Comparer if type equal to IList and use this factory. I'll add you example with implementation today/tomorrow. I think it will be useful for other framework users.

As an enhancement for the future I'll be thinking about adding some generic Comparers for lists to make handling cases like this easier.

ValeraT1982 commented 6 years ago

@Edimarcos I added your example (a bit simplified) to the list of examples (Example 4). You need version 1.2.1 to make it work because of a small issue in the default Factory implementation. Enjoy!!!! :)