C# 在单个 LINQ 语句中使用 .Select 和 .Where

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9410321/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 07:16:23  来源:igfitidea点击:

Using .Select and .Where in a single LINQ statement

c#linquniquedistinct

提问by robertviper08

I need to gather Distinct Id's from a particular table using LINQ. The catch is I also need a WHERE statement that should filter the results based only from the requirements I've set. Relatively new to having to use LINQ so much, but I'm using the following code more or less:

我需要使用 LINQ 从特定表中收集不同的 Id。问题是我还需要一个 WHERE 语句,该语句应该仅根据我设置的要求过滤结果。不得不如此频繁地使用 LINQ 相对较新,但我或多或少地使用了以下代码:

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

I tried to add .Distinct for var linq but it's still returning duplicate items (meaning having the same Id's). I've read through a lot of sites and have tried adding a .Select into the query but the .Where clause breaks instead. There are other articles where the query is somehow different with the way it retrieves the values and place it in a var. I also tried to use .GroupBy but I get an "At least one object must implement IComparable" when using Id as a key.

我试图为 var linq 添加 .Distinct 但它仍然返回重复的项目(意味着具有相同的 Id)。我已经阅读了很多网站,并尝试在查询中添加一个 .Select,但 .Where 子句却中断了。还有其他文章,其中查询与检索值并将其放入 var 的方式有些不同。我也尝试使用 .GroupBy 但在使用 Id 作为键时我得到一个“至少一个对象必须实现 IComparable”。

The query actually works and I'm able to output the data from the columns with the specifications I require, but I just can't seem to make .Distinct work (which is the only thing really missing). I tried to create two vars with one triggering a distinct call then have a nested foreach to ensure the values are just unique, but will thousands of records to gather the performance impact is just too much.

该查询实际上有效,我能够从具有我需要的规范的列中输出数据,但我似乎无法使 .Distinct 工作(这是唯一真正缺少的东西)。我试图创建两个变量,其中一个触发一个不同的调用,然后有一个嵌套的 foreach 以确保这些值是唯一的,但是数千条记录来收集性能影响是否太多了。

I'm unsure as well if I'd have to override or use IEnumerable for my requirement, and thought I'd ask the question around just in case there's an easier way, or if it's possible to have both .Select and .Where working in just one statement?

我也不确定是否必须覆盖或使用 IEnumerable 来满足我的要求,并认为我会问这个问题以防万一有更简单的方法,或者是否可以同时使用 .Select 和 .Where 工作在一个声明中?

采纳答案by Mark Byers

In order for Enumerable.Distinctto work for your type, you can implement IEquatable<T>and provide suitable definitions for Equalsand GetHashCode, otherwise it will use the default implementation: comparing for reference equality (assuming that you are using a reference type).

为了适用Enumerable.Distinct于您的类型,您可以IEquatable<T>Equalsand实现并提供合适的定义GetHashCode,否则它将使用默认实现:比较引用相等(假设您正在使用引用类型)。

From the manual:

从手册:

The Distinct(IEnumerable) method returns an unordered sequence that contains no duplicate values. It uses the default equality comparer, Default, to compare values.

The default equality comparer, Default, is used to compare values of the types that implement the IEquatable generic interface. To compare a custom data type, you need to implement this interface and provide your own GetHashCode and Equals methods for the type.

Distinct(IEnumerable) 方法返回一个不包含重复值的无序序列。它使用默认的相等比较器 Default 来比较值。

默认相等比较器 Default 用于比较实现 IEquatable 泛型接口的类型的值。要比较自定义数据类型,您需要实现此接口并为该类型提供您自己的 GetHashCode 和 Equals 方法。

In your case it looks like you might just need to compare the IDs, but you may also want to compare other fields too depending on what it means for you that two objects are "the same".

在您的情况下,您可能只需要比较 ID,但您可能还想比较其他字段,这取决于两个对象“相同”对您意味着什么。

You can also consider using DistinctByfrom morelinq.

您也可以考虑使用DistinctByfrom morelinq

Note that this is LINQ to Objects only, but I assume that's what you are using.

请注意,这只是 LINQ to Objects,但我认为这就是您正在使用的。

Yet another option is to combine GroupByand First:

另一种选择是结合GroupByFirst

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

This would also work in LINQ to SQL, for example.

例如,这也适用于 LINQ to SQL。

回答by Malmi

Do you passed a IEqualityComparer<T>to .Distinct()?

你通过了IEqualityComparer<T>.Distinct()

Something like this:

像这样的东西:

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())

回答by Flater

Did you add the Select()after the Where()or before?

你是Select()在之后Where()还是之前添加的?

You should add it after, because of the concurrency logic:

由于并发逻辑,您应该在后面添加它:

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

If you do a Select first, the Where clause can only contain the ID attribute because all other attributes have already been edited out.

如果您先执行 Select,则 Where 子句只能包含 ID 属性,因为所有其他属性都已被编辑掉。

Update: For clarity, this order of operators should work:

更新:为清楚起见,此运算符顺序应该有效:

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

Probably want to add a .toList()at the end but that's optional :)

可能想.toList()在最后添加一个,但这是可选的:)

回答by ScorpiAS

Since you are trying to compare two different objects you will need to first implement the IEqualityComparer interface. Here is an example code on a simple console application that uses distinct and a simple implementation of the IEqualityComparer:

由于您要比较两个不同的对象,因此您首先需要实现 IEqualityComparer 接口。下面是一个简单的控制台应用程序的示例代码,它使用了 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; }
    }
}

I hope that helps.

我希望这有帮助。