LINQ OrderBy的异常

时间:2011-12-13 10:12:37

标签: c# linq linq-to-objects linq-query-syntax

我还是LINQ的新手,我创建了一个我觉得有点笨拙的查询。我想知道是否有任何方法可以简化它?

我的查询如下:

var agreementsMatching = _ctx.LeasingAgreements
    .Where(x => x.Dealer.Id == dealer.dealerId)
    .ToList();
var ag = agreementsMatching
    .OrderBy(o => o.Model.Specification)
    .OrderBy(o => o.Model.ModelName)
    .OrderBy(o => o.Model.ModelBrand)
    .OrderBy(c => c.LeasingAgreementClicks)
    .GroupBy(sg => sg.Model.Specification)
    .Select(sg => new { GroupId = sg.Key, Agreements = sg });

我认为它可能不是最好的查询的原因还在于它给了我一个例外:

  

至少有一个对象必须实现IComparable。

我知道它发生是因为一个或多个对象没有实现IComparable接口。我只是不确定如何实际处理它。

非常感谢任何帮助/提示!

编辑:事实证明我不需要所有这些OrderBy来电。我可以这样做:

var agreementsMatching = _ctx.LeasingAgreements
    .Where(x => x.Dealer.Id == dealer.dealerId)
    .ToList();
var ag = agreementsMatching
    .GroupBy(sg => sg.Model.Specification)
    .Select(sg => new { GroupId = sg.Key, Agreements = sg });

虽然问题现在已经解决,但我仍然想学习如何避免上述错误:)

1 个答案:

答案 0 :(得分:8)

首先要注意的是

someList.OrderBy(item => item.SomeProp).OrderBy(item => item.SomeOtherProp);

或多或少等同于:

someList.OrderBy(item => item.SomeOtherProp);

因为第二个OrderBy撤消了第一个someList.OrderBy(item => item.SomeProp).ThenBy(item => item.SomeOtherProp); 的工作。通常你想要:

from item in someList orderby item.SomeProp, item.SomeOtherProp select item

请注意等效:

ThenBy

如上所述使用OrderBy

现在,使用任何语法,使用Linq到对象(但不是数据库和其他linq查询提供程序)IComparable<T>通过调用IComparable(如果可用),否则agreementsMatching (除非我们稍后会有例外)。由于OrderBy是内存中的列表,因此这是使用的表单。这就是int可以知道“order by”对于给定类型的含义。

字符串以及所有内置数字类型(doubleIComparable<T>等)都可以实现Specification,因此无需您采取任何操作,它们都可以正常使用。

据推测,您上面订购的至少一个属性不是这些类型中的一种,而是您自己的类型。我不知道你的代码中有哪些,所以我将补充以下内容:

我将假设Spec属性返回了一个Spec对象,并且Name个对象应按其class Spec { public property Name { get { /* code I don't care about here*/ } set { /* code I don't care about here*/ } } /* more code I don't care about here*/ } 属性排序,不区分大小写根据不变文化的方式。所以我开始:

IComparable<Spec>

我添加了IComparable的实现。在这种情况下实现class Spec : IComparable<Spec>, IComparable { public property Name { get { /* code I don't care about here*/ } set { /* code I don't care about here*/ } } /* more code I don't care about here*/ public int CompareTo(Spec other) { if(other == null) return 1; //Often we make use of an already-existing comparison, though not always return string.Compare(Name, other.Name, StringComparison.InvariantCultureIgnoreCase) } //For backwards compatibility: public int CompareTo(object other) { if(other == null) return 1; Spec os = other as Spec; if(os == null) throw new ArgumentException("Comparison between Spec and " + other.GetType().FullName + " is not allowed"); return CompareTo(os); } } 也是一个好主意,为了向后兼容,尽管可以跳过它。两者都定义了一个方法,用于将实例与另一个对象进行比较,返回一个数字&lt; 0如果实例是“较小”(按顺序排在第一位),如果它们在排序中是等效的则为0,如果实例为“更大”则为> 0;

OrderBy

现在,Spec可以处理List<Spec>.Sort()对象的比较,它将按名称进行比较(另外,我们可以使用IComparable<T>和其他一些东西。

最后一件事是,如果我们需要按其他规则排序,或者如果我们需要对我们没有源代码的类型进行排序而且它没有实现IComparable或{ {1}}?

在这里,我们可以创建一个实现IComparer<T>的类,这将绕过IComparable<T>的使用。这是一个证明这一点的例子:

public class OddBeforeEven : IComparer<int>
{
  public int Compare(int x, int y)
  {
    int compareOddEven = y % 2 - x % 2;
    if(compareOddEven != 0)
      return compareOddEven;
    //if both odd or both even, use default ordering:
    return x.CompareTo(y);
  }
}
/* ... */
var oddBeforeEven0To20 = Enumerable.Range(0, 21).OrderBy(x => x, new OddBeforeEven());
/*Enumerating oddBeforeEven0To20 will produce 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20*/

最后一点。

您确定在问题中需要ToList()吗?

Linq的新手经常打电话给ToList(),部分原因是为了让事情保持在一个他们可以想象得更好的结构中,部分是因为许多教程示例会大量使用它。

有时候ToList()是唯一合理的事情,有时需要从非内存形式的Linq中引入一些东西(例如针对数据库或XMLDocument)在记忆中。但是:

如果数据库中有东西出现,那么大部分时间最好尽可能长时间保存。这有很多例外,但通常希望将数据保存在数据库中并通过将其作为优化少数异常的内存进行优化,而不是习惯于快速将内容放入内存然后优化内存。将它保存在数据库中的时间百分比更快!

如果你需要切换到linq-to-objects,ToEnumerable()将在不急切执行查询的情况下执行此操作,因此在大多数情况下它优于ToList()

有时ToList()效率最高(特别是如果你必须两次点击相同的列表),但是当你看到它的明确需要时,你应该调用它,而不是默认。< / p>