web-dev-qa-db-fra.com

Utilisation de .Select et .Where dans une seule instruction LINQ

Je dois rassembler des identifiants distincts à partir d'une table particulière à l'aide de LINQ. Le problème est que j'ai également besoin d'une instruction WHERE qui devrait filtrer les résultats uniquement à partir des exigences que j'ai définies. Relativement nouveau pour avoir à utiliser LINQ tellement, mais j'utilise plus ou moins le code suivant:

private void WriteStuff(SqlHelper db, EmployeeHelper emp)
{
    String checkFieldChange;
    AnIList tableClass = new AnIList(db, (int)emp.PersonId);
    var linq = tableClass.Items
        .Where(
           x => x.UserId == emp.UserId 
             && x.Date > DateBeforeChanges 
             && x.Date < DateAfterEffective 
             && (
                     (x.Field == Inserted)
                  || (x.Field == Deleted)))
                )
             ).OrderByDescending(x => x.Id);

    if (linq != null)
    {
        foreach (TableClassChanges item in linq)
        {
            AnotherIList payTxn = new AnotherIList(db, item.Id);
            checkFieldChange = GetChangeType(item.FieldName);

            // Other codes that will retrieve data from each item 
            // and write it into a text file
        }
    }
}

J'ai essayé d'ajouter .Distinct pour var linq mais il renvoie toujours les éléments en double (ce qui signifie qu'ils ont le même identifiant). J'ai parcouru de nombreux sites et essayé d'ajouter un .Select à la requête, mais la clause .Where est rompue à la place. Il y a d'autres articles où la requête est quelque peu différente avec la façon dont elle récupère les valeurs et la place dans un var. J'ai également essayé d'utiliser .GroupBy mais j'obtiens un "Au moins un objet doit implémenter IComparable" lorsque j'utilise Id comme clé.

La requête fonctionne réellement et je suis en mesure de sortir les données des colonnes avec les spécifications requises, mais je n'arrive pas à effectuer le travail .Distinct (ce qui est la seule chose qui manque vraiment). J'ai essayé de créer deux vars, l'un d'entre eux déclenchant un appel distinct, puis un foreach imbriqué pour garantir des valeurs uniques, mais des milliers d'enregistrements rassemblant l'impact sur les performances sont tout simplement trop importants.

Je ne sais pas non plus si je devrais remplacer ou utiliser IEnumerable pour mon besoin et je pensais poser la question au cas où il y aurait un moyen plus facile, ou s'il est possible d'avoir les deux .Select et .Where qui fonctionnent dans une seule déclaration?

25
robertviper08

Pour Enumerable.Distinct pour travailler avec votre type, vous pouvez implémenter IEquatable<T> et fournit les définitions appropriées pour Equals et GetHashCode, sinon il utilisera l’implémentation par défaut: compare pour l’égalité de référence (en supposant que vous utilisez un type de référence).

Du manuel:

La méthode Distinct (IEnumerable) renvoie une séquence non ordonnée qui ne contient aucune valeur en double. Il utilise le comparateur d'égalité par défaut, Default, pour comparer les valeurs.

Le comparateur d'égalité par défaut, Default, est utilisé pour comparer les valeurs des types qui implémentent l'interface générique IEquatable. Pour comparer un type de données personnalisé, vous devez implémenter cette interface et fournir vos propres méthodes GetHashCode et Equals pour ce type.

Dans votre cas, il semblerait que vous deviez peut-être simplement comparer les ID, mais vous pouvez également vouloir comparer d'autres champs aussi, en fonction de ce que cela signifie pour vous que deux objets sont "identiques".

Vous pouvez également envisager d'utiliser DistinctBy à partir de morelinq .

Notez qu'il ne s'agit que de LINQ to Objects, mais je suppose que c'est ce que vous utilisez.

Une autre option consiste à combiner GroupBy et First:

 var query = // your query here...
    .GroupBy(x => x.Id)
    .Select(g => g.First());

Cela fonctionnerait également dans LINQ to SQL, par exemple.

11
Mark Byers

Avez-vous ajouté le Select() après le Where() ou avant?

Vous devriez l'ajouter après, à cause de la logique de concurrence:

 1 Take the entire table  
 2 Filter it accordingly  
 3 Select only the ID's  
 4 Make them distinct.  

Si vous effectuez d'abord une sélection, la clause Where ne peut contenir que l'attribut ID car tous les autres attributs ont déjà été supprimés.

Mise à jour: pour plus de clarté, cet ordre des opérateurs doit fonctionner:

db.Items.Where(x=> x.userid == user_ID).Select(x=>x.Id).Distinct();

Veut probablement ajouter un .toList() à la fin mais c'est optionnel :)

38
Flater

Puisque vous essayez de comparer deux objets différents, vous devez d'abord implémenter l'interface IEqualityComparer. Voici un exemple de code sur une application console simple utilisant une implémentation distincte et simple de IEqualityComparer:

 class Program
{
    static void Main(string[] args)
    {
        List<Test> testData = new List<Test>()
        {
            new Test(1,"Test"),
            new Test(2, "Test"),
            new Test(2, "Test")
        };

        var result = testData.Where(x => x.Id > 1).Distinct(new MyComparer());
    }
}

public class MyComparer : IEqualityComparer<Test>
{
    public bool Equals(Test x, Test y)
    {
        return x.Id == y.Id;
    }

    public int GetHashCode(Test obj)
    {
        return string.Format("{0}{1}", obj.Id, obj.Name).GetHashCode();
    }
}


public class Test
{
    public Test(int id, string name)
    {
        this.id = id;
        this.name = name;
    }

    private int id;

    public int Id
    {
        get { return id; }
        set { id = value; }
    }
    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}

J'espère que ça aide.

2
ScorpiAS

Avez-vous passé un IEqualityComparer<T> À .Distinct()?

Quelque chose comme ça:

internal abstract class BaseComparer<T> : IEqualityComparer<T> {
    public bool Equals(T x, T y) {
        return GetHashCode(x) == GetHashCode(y);
    }

    public abstract int GetHashCode(T obj);
}

internal class DetailComparer : BaseComparer<StyleFeatureItem> {
    public override int GetHashCode(MyClass obj) {
        return obj.ID.GetHashCode();
    }
}

Usage:

list.Distinct(new DetailComparer())
1
Malmi