Lista.Exceito não está funcionando

Eu tento subtrair 2 listas como abaixo do código, assignUsers tem 3 registros e os assignUsers assignedUsers tem 2 linhas. Após o método Except , ainda recebo 3 linhas, embora eu deva obter 1 registro, porque 2 linhas em assignUsers assignedUsers são semelhantes a assignUsers

  var users = accountApp.GetUsersByAccountId(context.GetUserData().AccountId); List assignUsers = Mapper.Map<List>(users).ToList(); var mailUsers = mailApp.GetMailAssignedByMailId(id).Select(m => new { m.UserId, m.User.Name }).ToList(); List assignedUsers = mailUsers.Select(Mapper.DynamicMap).ToList(); assignUsers = assignUsers.Except(assignedUsers).ToList(); 

Para tornar o método Except funcionando conforme o esperado, a class AssignUserViewModel deve ter os methods GetHashCode e Equals corretamente substituídos.

Por exemplo, se os objects AssignUserViewModel forem exclusivamente definidos por seu Id , você deve definir a class desta maneira:

 class AssignUserViewModel { // other methods... public override int GetHashCode() { return this.Id.GetHashCode(); } public override bool Equals(object obj) { if (!(obj is AssignUserViewModel)) throw new ArgumentException("obj is not an AssignUserViewModel"); var usr = obj as AssignUserViewModel; if (usr == null) return false; return this.Id.Equals(usr.Id); } } 

Caso contrário, se você não puder / não quiser alterar a implementação da class, você pode implementar um IEqualityComparer<> e passá-lo para o método Except , por exemplo:

 class AssignUserViewModelEqualityComparer : IEqualityComparer { public bool Equals(AssignUserViewModel x, AssignUserViewModel y) { if (object.ReferenceEquals(x, y)) return true; if(x == null || y == null) return false; return x.Id.Equals(y.Id); } public int GetHashCode(AssignUserViewModel obj) { return obj.Id.GetHashCode(); } } 

então sua última linha se tornaria:

 assignUsers = assignUsers.Except(assignedUsers, new AssignUserViewModelEqualityComparer()).ToList(); 

Por que isso está acontecendo? Quando você usa Set Operations (Distinct, Exceto, Intersect, Union), o Linq precisa comparar elementos de seqüência (s) para igualdade. Por padrão, o Linq usa os methods Object.Equals e Object.GetHashCode para comparar elementos. Se esses methods não forem substituídos em seu tipo, a implementação da class base será usada, o que compara objects por igualdade de referência. A implementação padrão garante que dois objects com a mesma referência tenham o mesmo código hash (assim considerado igual). Este é o seu caso. Mapper class Mapper cria novas instâncias de objects AssignUserViewModel , que possuem referências diferentes e não podem ser tratadas como iguais (mesmo que todos os valores de campo sejam iguais).

Então, o que poderíamos fazer com isso?

  • Substitua os methods Equals e GetHashCode na sua turma. Cabe a você como você vai tratar a igualdade de objects – todos os campos, ou apenas identidade. O Linq usará seus methods para comparar elementos.

  • Forneça seu próprio comparador (isso geralmente é o caso quando você não pode modificar seu object e sobrescrever Equals e GetHashCode . Sim, todas as operações do Linq Set possuem duas sobrecargas – uma que usa o comparador padrão e outra que aceita seu IEqualityComparer .

  • Use tipos anônimos. Todos os tipos anônimos já geraram os methods Equals e GetHashCode , que usam a comparação de todas as propriedades para determinar se os objects são iguais. Nesse caso, você não precisa modificar o seu tipo nem criar um comparador.

Assim você já tem amostras das duas primeiras abordagens, aqui está a última:

 var assignUsers = accountApp.GetUsersByAccountId(context.GetUserData().AccountId) .Select(u => new { u.UserId, u.Name }); var assignedUsers = mailApp.GetMailAssignedByMailId(id) .Select(m => new { m.UserId, m.User.Name }); var assignUsers = assignUser.Except(assignedUsers); // do not map until here List result = assignUsers.Select(Mapper.DynamicMap).ToList();