简体   繁体   中英

Custom IEqualityComparer to calculate Difference between two lists using LINQ Except

I am using this answer Difference between two lists to find a difference between two lists using Linq Except . However, my objects in the lists are of generic type, so I wrote a Comparer for the generic types, then I pass it to Except method. However I receive the following error message:

Error CS1929 'List' does not contain a definition for 'Except' and the best extension method overload 'ParallelEnumerable.Except(ParallelQuery, ParallelQuery, IEqualityComparer)' requires a receiver of type 'ParallelQuery'

My call:

var differenceList = list1.Except(list2, new PropertyComparer<Guid>("ObjectId"));

And my comparer:

public class PropertyComparer<T> : IEqualityComparer<T>
{
    private PropertyInfo _PropertyInfo;
    public PropertyComparer(string propertyName)
    {
        _PropertyInfo = typeof(T).GetProperty(propertyName, 
            BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
        if (_PropertyInfo == null)
        {
            throw new ArgumentException(
                string.Format("{0} is not a property of type {1}.", propertyName, typeof(T)));
        }
    }

    public int GetHashCode(T obj)
    {
        object propertyValue = _PropertyInfo.GetValue(obj, null);
        if (obj == null) return 0;
        return propertyValue.GetHashCode();
    }

    public bool Equals(T x, T y)
    {
        object xValue = _PropertyInfo.GetValue(x, null);
        object yValue = _PropertyInfo.GetValue(y, null);

        if (xValue == null)
        {
            return yValue == null;
        }

        return xValue.Equals(yValue);
    }
}

And from the declaration of Except method in System.Linq , which fits my comparer:

public static IEnumerable<TSource> Except<TSource>(
    this IEnumerable<TSource> first, 
    IEnumerable<TSource> second, 
    IEqualityComparer<TSource> comparer
);

What am I doing wrong and how can I get it working?


UPDATE

list1 and list2 types:

List<Contact>

Where Contact is

public class Contact : EntityBase
{
    public Guid ContactId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public List<Email> Emails {get; set;}
}

EntityBase is

public class EntityBase : IEntity
{
    public IQueryable<T> GetDBEntities<T>(ApplicationDbContext db) where T : class
    {
        return db.Set<T>();
    }

    public List<T> GetLocalEntities<T>() where T : class
    {
        var localProperties = this.GetType().GetProperties();
        foreach (var localProperty in localProperties)
        {
            var localPropertyValue = localProperty.GetValue(this);
            if (localPropertyValue != null && localPropertyValue.IsGenericList() == true)
            {
                var localPropertyValueType = localPropertyValue.GetType(); // List<object>
                var localPropertyValueTypeDecoupled = localPropertyValueType.GetGenericArguments().Single(); // List<T>
                if (localPropertyValueTypeDecoupled == typeof(T))
                {
                    return (List<T>)localPropertyValue;
                }
            }
        }
        throw new Exception("Entity Types Validation Error");
    }

    public void ProcessEntityReference<T>(ApplicationDbContext db) where T : class
    {
        // T is Email
        var remoteList = this.GetDBEntities<T>(db).ToList();
        var updatedList = GetLocalEntities<T>();
        var toBeAdded = updatedList.Except(remoteList, new PropertyComparer<Guid>("ContactId"));
        var toBeDeleted = new List<object>();
        throw new NotImplementedException();
    }

    public void ProcessEntityReferences(ApplicationDbContext db)
    {
        this.ProcessEntityReference<Email>(db);
    }
}

You should pass the same type used by your list to the Except method, in your example, you are using Guid but it should be of type Contact , also, your Contact class doesn't have a property called "ObjectId", try changing that for " ContactId ", the following seems to work fine:

static void Main(string[] args)
{
     var list1 = new List<Contact>();
     list1.Add(new Contact() { ContactId = Guid.Parse("FB58F102-0CE4-4914-ABFF-ABBD3895D719") });
     list1.Add(new Contact() { ContactId = Guid.Parse("5A201238-6036-4385-B848-DEE598A3520C") });

     var list2 = new List<Contact>();
     list2.Add(new Contact() { ContactId = Guid.Parse("FB58F102-0CE4-4914-ABFF-ABBD3895D719") });

     var list3 = list1.Except(list2, new PropertyComparer<Contact>("ContactId"));

     foreach (var item in list3) 
         Console.WriteLine(item.ContactId.ToString());

     Console.ReadLine();
}

public class Contact
{
    public Guid ContactId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class PropertyComparer<T> : IEqualityComparer<T>
{
    private PropertyInfo _PropertyInfo;
    public PropertyComparer(string propertyName)
    {
        _PropertyInfo = typeof(T).GetProperty(propertyName,
            BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
        if (_PropertyInfo == null)
        {
            throw new ArgumentException(
                string.Format("{0} is not a property of type {1}.", propertyName, typeof(T)));
        }
    }

    public int GetHashCode(T obj)
    {
        object propertyValue = _PropertyInfo.GetValue(obj, null);
        if (obj == null) return 0;
        return propertyValue.GetHashCode();
    }

    public bool Equals(T x, T y)
    {
        object xValue = _PropertyInfo.GetValue(x, null);
        object yValue = _PropertyInfo.GetValue(y, null);

        if (xValue == null)
        {
            return yValue == null;
        }

        return xValue.Equals(yValue);
    }
}

Output:

5a201238-6036-4385-b848-dee598a3520c

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM