【问题标题】:Using .Select and .Where in a single LINQ statement在单个 LINQ 语句中使用 .Select 和 .Where
【发布时间】:2012-02-23 09:34:07
【问题描述】:

我需要使用 LINQ 从特定表中收集 Distinct 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
        }
    }
}

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

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

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

【问题讨论】:

  • 您的问题似乎暗示表中有多行具有相同的ID,但数据不同-这不完全合乎逻辑。请说明您的数据是什么样的,以及这些 id 到底发生了什么。
  • 嗨乔恩,这实际上是正确的。此处的 Id 是一个事务 ID,并在单个命令中跟踪已更改为特定用户的所有内容(即字段 a、b 和 c 是通过此事务 ID 编辑的)。我需要区分它,因为唯一需要的数据是更新给该特定用户的最新行,因此无论该单个事务中是否有多个编辑,只需要最近的更改。

标签: c# linq unique distinct


【解决方案1】:

您是在Where() 之后还是之前添加了Select()

由于并发逻辑,你应该在之后添加它:

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

如果先选择,Where 子句只能包含 ID 属性,因为所有其他属性都已经被编辑掉了。

更新:为了清楚起见,这个运算符的顺序应该可以工作:

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

可能想在末尾添加.toList(),但这是可选的:)

【讨论】:

  • 实际上我尝试了这种方法,每当添加 Select 时我都会收到 ArgumentException(至少一个对象必须实现 IComparable)
  • 这对我有帮助,只是在寻找一个带有 WHERE 的简单 SELECT。
【解决方案2】:

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

来自手册:

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

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

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

你也可以考虑使用morelinq中的DistinctBy

请注意,这只是 LINQ to Objects,但我假设您正在使用它。

另一种选择是将GroupByFirst 结合起来:

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

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

【讨论】:

  • 今天早上我发现 morelinq 时想使用它,但不允许将其包含在解决方案中。现在看一下 groupby 和 first 选项。
【解决方案3】:

由于您尝试比较两个不同的对象,您需要首先实现 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; }
    }
}

希望对你有帮助。

【讨论】:

  • 我实际上已经在阅读其他链接以及 IEqualityComparer 的其余部分时尝试过这一点。然而,问题是我也会在其他表上执行相同的逻辑,而且比较列并不总是 Id。我可以采用这种方法,但我担心这将花费更多时间,而不是为我将与之交互的所有表进行整理。
  • 你可以让你的表实现一个公共基类,只使用它们相同的属性。这样,您将能够基于基类而不是每个表编写比较器。因此,例如,如果您有 10 个具有 ID 的表和 20 个具有 FirstName 的表,您只需编写两个基于两个基类的比较器(一个包含 ID 属性,另一个包含 FirstName)。
【解决方案4】:

您是否将IEqualityComparer&lt;T&gt; 传递给.Distinct()

类似这样的:

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

用法:

list.Distinct(new DetailComparer())

【讨论】:

    【解决方案5】:

    您可以像这样使用 LINQ 轻松查询

    考虑到这个 JSON

    {
        "items": [
            {
                "id": "10",
                "name": "one"
            },
            {
                "id": "12",
                "name": "two"
            }
        ]
    }
    

    像这样把它放在一个名为json的变量中,

    JObject json = JObject.Parse("{'items':[{'id':'10','name':'one'},{'id':'12','name':'two'}]}");
    

    您可以使用以下 LINQ 查询从名称为 "one" 的项目中选择所有 ID

    var Ids =
        from item in json["items"]
        where (string)item["name"] == "one"
        select item["id"];
    

    然后,您将在 IEnumerable 列表中获得结果

    【讨论】:

      猜你喜欢
      • 2021-01-30
      • 2017-05-01
      • 1970-01-01
      • 2015-07-26
      • 2011-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多