1

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);
    }
}
Raman Sinclair
  • 1,194
  • 17
  • 31

1 Answers1

3

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
Isma
  • 14,604
  • 5
  • 37
  • 51
  • 2
    The main problem was that `new PropertyComparer("ContactId")` should take a type of `Contact` to match the type parameter of the lists. OP was originally using `Guid` as the type parameter which is what caused the error – devNull Dec 18 '19 at 22:58
  • I understand now that I should have used `Contact` instead of `Guid` as the type parameter, however changing it to `Contact` doesn't resolve the problem. The error text now is `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'` – Raman Sinclair Dec 18 '19 at 23:00
  • @devNull, same error, only type was updated. Should I create a separate question for that? – Raman Sinclair Dec 18 '19 at 23:04
  • Clean your project, delete the bin folder and rebuild, also, create a new empty console project and copy paste the code from the answer. It seems to work.... – Isma Dec 18 '19 at 23:04
  • @Isma, cleaned the project, vs cache, etc. Still no luck. The code mentioned works in the console project, but not in the original project. In the original project I work with generic types and use reflection to iterate over the object properties to get them, but I don't know if this is the cause of the problem, though. – Raman Sinclair Dec 18 '19 at 23:09
  • 1
    I will accept this answer since it addresses one of the problems and I will create another question later to seek help with the current error. Thank you! – Raman Sinclair Dec 18 '19 at 23:10