ValeraT1982 / ObjectsComparer

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

ArgumentException comparing two objects #31

Closed luismiguelferreira closed 3 years ago

luismiguelferreira commented 3 years ago

Hi.

I'm receiving System.ArgumentException: obj1 when comparing two objects. Can you please help me identify the root cause?

Error: System.ArgumentException: obj1 at ObjectsComparer.DynamicValueComparer1.IsArgumentException(Object obj, String argumentName) at ObjectsComparer.DynamicValueComparer1.Compare(Object obj1, Object obj2, ComparisonSettings settings) at ObjectsComparer.Comparer1.CalculateDifferences(T obj1, T obj2, MemberInfo memberInfo)+MoveNext() at System.Linq.Enumerable.Any[TSource](IEnumerable1 source) at ObjectsComparer.AbstractComparer1.Compare(T obj1, T obj2)`

ValeraT1982 commented 3 years ago

@luismiguelferreira can you please share your code?

luismiguelferreira commented 3 years ago

I'm comparing two objects (ProductDocument):

public bool Compare(ProductDocument source, ProductDocument destination)
        {
            var comparerFactory = new CompareObjectsFactory();
            var comparer = comparerFactory.GetObjectsComparer<ProductDocument>(new ObjectsComparer.ComparisonSettings { EmptyAndNullEnumerablesEqual = true });

            return !(source == null || destination == null) && comparer.Compare(source, destination);
        }

CompareObjectsFactory:

public class CompareObjectsFactory : ComparersFactory
    {
        public override IComparer<T> GetObjectsComparer<T>(ComparisonSettings? settings = null, BaseComparer? parentComparer = null)
        {
            if (typeof(T) == typeof(ProductDocument))
            {
                return (IComparer<T>)new ProductDocumentComparerBuilder().Create(settings, parentComparer, this);
            }

            return base.GetObjectsComparer<T>(settings, parentComparer);
        }
    }

ProductDocumentComparerBuilder:

public class ProductDocumentComparerBuilder : ICustomComparer<ProductDocument>
    {
        public ObjectsComparer.IComparer<ProductDocument> Create(
            ComparisonSettings? settings = null,
            BaseComparer? parentComparer = null,
            IComparersFactory? factory = null)
        {
            var comparer = new ObjectsComparer.Comparer<ProductDocument>(settings, parentComparer, factory);

            AddSpecificComparers(comparer);

            return comparer;
        }

        private void AddSpecificComparers(ObjectsComparer.Comparer<ProductDocument> comparer)
        {
            comparer.IgnoreMember(() => new ProductDocument().DocumentId);
            comparer.IgnoreMember(() => new ProductDocument().Version);

            comparer.AddComparerOverride(
               () => new ProductDocument().Colors,
               (c1, c2, parentSettings) =>
               {
                   var cl1 = c1.OrderBy(c => c.Id).ToList();
                   var cl2 = c2.OrderBy(c => c.Id).ToList();
                   return new ObjectsComparer.Comparer<List<Color>>().Compare(cl1, cl2);
               }
           );
       }
ValeraT1982 commented 3 years ago

Can you please add stacktrace for the error?

luismiguelferreira commented 3 years ago

Relevant info of stacktrace:

System.ArgumentException: obj1
   at ObjectsComparer.DynamicValueComparer`1.IsArgumentException(Object obj, String argumentName)
   at ObjectsComparer.DynamicValueComparer`1.Compare(Object obj1, Object obj2, ComparisonSettings settings)
   at ObjectsComparer.Comparer`1.CalculateDifferences(T obj1, T obj2, MemberInfo memberInfo)+MoveNext()
   at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source)
   at ObjectsComparer.AbstractComparer`1.Compare(T obj1, T obj2)
   at Application.Service.Handlers.Events.InventoryEventHandler.<>c__DisplayClass3_0.<<Handle>b__0>d.MoveNext() in /home/jenkins/agent/workspace/_eventhandler_build_master/src/Application.Service/Handlers/Events/InventoryEventHandler.cs:line 74
--- End of stack trace from previous location where exception was thrown ---
ValeraT1982 commented 3 years ago

What is the type of ProductDocument.Colors? It looks like one of conditions here is false

luismiguelferreira commented 3 years ago

IList

ValeraT1982 commented 3 years ago

Try return new ObjectsComparer.Comparer<IList>().Compare(cl1, cl2); not return new ObjectsComparer.Comparer<List>().Compare(cl1, cl2);

luismiguelferreira commented 3 years ago

Hi, @ValeraT1982. I tried this solution but it's not solving the problem.

ValeraT1982 commented 3 years ago

Can you please share ProductDocument class code?

luismiguelferreira commented 3 years ago
public class ProductDocument { 

    public ProductDocument();

    public Guid ProductId { get; set; }

    public Collection? Collection { get; set; }

    public IList<Color> Colors { get; set; }

    public string? PartNumber { get; set; }

    public Brand? Brand { get; set; }
}
ValeraT1982 commented 3 years ago

Really interesting case. It happens when Colors equal to null. AddComparerOverride with expression as a parameter cannot be used in this case. You need to create a custom value comparer.

public class ColorsComparer : AbstractValueComparer<IList<Color>>
{
    public override bool Compare(IList<Color> c1, IList<Color> c2, ComparisonSettings settings)
    {
        var cl1 = c1?.OrderBy(c => c.Id).ToList() ?? new List<Color>();
        var cl2 = c2?.OrderBy(c => c.Id).ToList() ?? new List<Color>();

        return new ObjectsComparer.Comparer<IList<Color>>().Compare(cl1, cl2);
    }
}
comparer.AddComparerOverride(
   () => new ProductDocument().Colors,
   new ColorsComparer()
);

I'll try to fix it in the next versions.

luismiguelferreira commented 3 years ago

Thanks @ValeraT1982 .