J'ai des cloches dans ma base de données avec le même numéro. Je veux tous les obtenir sans duplication. Ensuite, je crée une classe de comparaison pour effectuer ce travail, mais l'exécution de la fonction crée un retard important par rapport à la fonction distincte, de 0,6 à 3,2 secondes!
Est-ce que je le fais bien ou je dois utiliser une autre méthode?
reg.AddRange((from a in this.dataContext.reglements
join b in this.dataContext.Clients on a.Id_client equals b.Id
where a.date_v <= datefin && a.date_v >= datedeb
where a.Id_client == b.Id
orderby a.date_v descending
select new Class_reglement
{
nom = b.Nom,
code = b.code,
Numf = a.Numf,
}).AsEnumerable().Distinct(new Compare()).ToList());
class Compare : IEqualityComparer<Class_reglement>
{
public bool Equals(Class_reglement x, Class_reglement y)
{
if (x.Numf == y.Numf)
{
return true;
}
else { return false; }
}
public int GetHashCode(Class_reglement codeh)
{
return 0;
}
}
Pas étonnant, compte tenu de votre implémentation GetHashCode
qui renvoie toujours la même valeur. Distinct
s'appuie sur une bonne fonction de hachage pour fonctionner efficacement.
Lorsque vous implémentez des interfaces de classes , vous devez lire leur documentation first, sinon vous ne savez pas quel contrat vous êtes censé implémenter.1
Dans votre code, la solution consiste à transmettre GetHashCode
à Class_reglement.Numf.GetHashCode
et à le mettre en œuvre de manière appropriée à cet endroit.
En dehors de cela, votre méthode Equals
est pleine de code inutile. Il pourrait être réécrit comme suit (même sémantique, ¼ du code, plus lisible):
public bool Equals(Class_reglement x, Class_reglement y)
{
return x.Numf == y.Numf;
}
En outre, l’appel ToList
est inutile et prend beaucoup de temps: AddRange
accepte les IEnumerable
et la conversion en List
n’est donc pas requise. AsEnumerable
est aussi _ redondant ici puisque le traitement du résultat dans AddRange
le provoquera de toute façon.
1 Implémenter du code sans savoir ce qu’il fait réellement s’appelle programmation culte du fret . C’est une pratique étonnamment répandue. Cela ne fonctionne pas fondamentalement.
Essayez ce code:
public class GenericCompare<T> : IEqualityComparer<T> where T : class
{
private Func<T, object> _expr { get; set; }
public GenericCompare(Func<T, object> expr)
{
this._expr = expr;
}
public bool Equals(T x, T y)
{
var first = _expr.Invoke(x);
var sec = _expr.Invoke(y);
if (first != null && first.Equals(sec))
return true;
else
return false;
}
public int GetHashCode(T obj)
{
return obj.GetHashCode();
}
}
Exemple d'utilisation:
collection = collection
.Except(ExistedDataEles, new GenericCompare<DataEle>(x=>x.Id))
.ToList();
L'inclusion de votre classe de comparaison (ou plus précisément de l'appel AsEnumerable
que vous deviez utiliser pour le faire fonctionner) signifiait que la logique de tri passait du serveur de base de données à celui du client de base de données (votre application). Cela signifie que votre client doit maintenant extraire puis traiter un plus grand nombre d'enregistrements, ce qui sera toujours moins efficace que d'effectuer la recherche dans la base de données où les index appropriés peuvent être utilisés.
Vous devriez essayer de développer une clause where qui satisfasse vos exigences, voir Utilisation d’un IEqualityComparer avec un LINQ to Entities Except clause pour plus de détails.
Si vous voulez une solution générique sans boxe:
public class KeyBasedEqualityComparer<T, TKey> : IEqualityComparer<T>
{
private readonly Func<T, TKey> _keyGetter;
public KeyBasedEqualityComparer(Func<T, TKey> keyGetter)
{
_keyGetter = keyGetter;
}
public bool Equals(T x, T y)
{
return EqualityComparer<TKey>.Default.Equals(_keyGetter(x), _keyGetter(y));
}
public int GetHashCode(T obj)
{
TKey key = _keyGetter(obj);
return key == null ? 0 : key.GetHashCode();
}
}
public static class KeyBasedEqualityComparer<T>
{
public static KeyBasedEqualityComparer<T, TKey> Create<TKey>(Func<T, TKey> keyGetter)
{
return new KeyBasedEqualityComparer<T, TKey>(keyGetter);
}
}
usage:
KeyBasedEqualityComparer<Class_reglement>.Create(x => x.Numf)
Juste du code, avec implémentation des validations GetHashCode
et NULL
:
public class Class_reglementComparer : IEqualityComparer<Class_reglement>
{
public bool Equals(Class_reglement x, Class_reglement y)
{
if (x is null || y is null))
return false;
return x.Numf == y.Numf;
}
public int GetHashCode(Class_reglement product)
{
//Check whether the object is null
if (product is null) return 0;
//Get hash code for the Numf field if it is not null.
int hashNumf = product.hashNumf == null ? 0 : product.hashNumf.GetHashCode();
return hashNumf;
}
}
Exemple: Liste de Class_reglement distinct par Numf
List<Class_reglement> items = items.Distinct(new Class_reglementComparer());