web-dev-qa-db-fra.com

Comment utiliser IEqualityComparer

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;
        }

    }
82
Akrem

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.

161
Konrad Rudolph

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(); 
41
suneelsarraf

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.

2
Justin

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)
1
user764754

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());
1
Shahar Shokrani